文章目录

    • 介绍
    • enqueueMessage 重要源码
    • next 重要源码
    • Linux的pipe/epoll机制

介绍

基于Android 9.0

MessageQueue,消息队列,在消息机制的作用是维护一个Message的队列,供Looper使用。MessageQueuede 通过一个单链表(即mMessages)来实现队列,由于Message本身就有一个指向下一个Message的Message变量next,所以MessageQueue不需要再借助其他结构来实现队列,只需要一个mMessages变量指向链表的头节点就可以了。MessageQueue中的关键方法是enqueueMessagenext,前者用于增加Message,后者用于读取Message。


enqueueMessage 重要源码

boolean enqueueMessage(Message msg, long when) {if (msg.target == null) {            throw new IllegalArgumentException("Message must have a target.");        }        if (msg.isInUse()) {            throw new IllegalStateException(msg + " This message is already in use.");        }        synchronized (this) {   .........            msg.markInUse();            msg.when = when;            Message p = mMessages;            boolean needWake;            if (p == null || when == 0 || when < p.when) {                msg.next = p;1)                mMessages = msg;                needWake = mBlocked;            } else {                needWake = mBlocked && p.target == null && msg.isAsynchronous();                Message prev;                for (;;) {2)                    prev = p;                    p = p.next;                    if (p == null || when < p.when) {                        break;                    }                    if (needWake && p.isAsynchronous()) {                        needWake = false;                    }                }                msg.next = p; // invariant: p == prev.next                prev.next = msg;            }.........        return true;    }

可以看到,enqueueMessage的作用是将一个新的Message插入到链表中正确的位置,这个链表是有序的,属性when(应处理时间)越小,位置越前,所以 (1): 如果新的Message的时间点比根节点前,就成为新的根节点,否则 (2): 插入链表中的正确位置。而在插入之前,会检查Message是不是设置了target或者是否已经使用过了,所以Handle接收到的Message不能直接再次发送出去。


next 重要源码

  Message next() {      ......        int pendingIdleHandlerCount = -1; // -1 only during first iteration        int nextPollTimeoutMillis = 0;        for (;;) {            .......            nativePollOnce(ptr, nextPollTimeoutMillis);(1)            synchronized (this) {                final long now = SystemClock.uptimeMillis();                Message prevMsg = null;                Message msg = mMessages;                if (msg != null && msg.target == null) {                    // Stalled by a barrier.  Find the next asynchronous message in the queue.                    do {                        prevMsg = msg;                        msg = msg.next;                    } while (msg != null && !msg.isAsynchronous());                }                if (msg != null) {                    if (now < msg.when) {                        // Next message is not ready.  Set a timeout to wake up when it is ready.                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);(2)                    } else {(3)                        // Got a message.                        mBlocked = false;                        if (prevMsg != null) {                            prevMsg.next = msg.next;                        } else {                            mMessages = msg.next;                        }                        msg.next = null;                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);                        msg.markInUse();                        return msg;                    }                } else {                    // No more messages.                    nextPollTimeoutMillis = -1;                }                // Process the quit message now that all pending messages have been handled.                if (mQuitting) {(4)                    dispose();                    return null;                }.......            }          ........            // Reset the idle handler count to 0 so we do not run them again.            pendingIdleHandlerCount = 0;            // While calling an idle handler, a new message could have been delivered            // so go back and look again for a pending message without waiting.            nextPollTimeoutMillis = 0;        }    }

next中的大部分代码都在一个for循环中,以此来寻找链表中的可以处理的Message,(1)(2) 位置的代码用于在当前没有Message需要处理时休眠(机制下节再说),所以next方法是堵塞的。在 (3) 处,获得当前时间可以处理的Message后返回;在 (4) 处,如果这个线程结束了,就返回null表示这个队列停止使用。


Linux的pipe/epoll机制

参考知乎问答:Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。

更多相关文章

  1. Handler消息传送机制
  2. 解决-Android: ListView.setSelection无效
  3. Android(安卓)Handler的基本使用
  4. [Android开发] 代码code设置9.png/9-patch 图片背景后,此view中的
  5. 【Android】快速切换到主线程更新UI的几种方法
  6. Android(安卓)Java 线程池 ThreadPoolExecutor源码篇
  7. Android(安卓)内存管理机制
  8. android review--基础知识
  9. 细读《深入理解 Android(安卓)内核设计思想》(一)进程间通信与同步

随机推荐

  1. android蚂蚁金服支付宝支付集成步骤
  2. Android(安卓)模拟按键事件(KeyEvent)
  3. html基础:标签与段落,列表,表格,表单
  4. android基础(1)
  5. Android开发从初级到高级学习路线
  6. android拍照、图库与裁剪
  7. 国内优秀Android学习资源汇总全集
  8. Dagger2使用详解
  9. Android:实现带图片和CheckBox的ListView
  10. Android(安卓)Studio 自定义快捷注释模板