Message 内部维持了一个链表缓存池来避免重复创建 Message 对象造成的额外消耗,以静态属性 Message sPool 作为缓存池链表头,Message next 作为链表的 next 指针。同样在MessageQueue 中用Message mMessages 用来维持消息队列的链表头。
MessageQueue----保存待处理消息队列,解决数据并发。
源码中,MessageQueue利用JNI方式调用了多个 C/C++方法(该方法使用关键字 native 声明),即java控制线程是否阻塞,C/C++来实现功能。主要的Native方法:
// 初始化 private native static long nativeInit(); // 注销 private native static void nativeDestroy(long ptr); // 阻塞线程 timeoutMillis 毫秒 private native void nativePollOnce(long ptr, int timeoutMillis); // 唤醒线程 private native static void nativeWake(long ptr); // 线程是否处于阻塞状态 private native static boolean nativeIsPolling(long ptr); // 设置文件描述符 private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
MessageQueue的创建
// 当前 MessageQueue 是否可以退出 private final boolean mQuitAllowed; // native 层中 NativeMessageQueue 队列指针的地址, mPtr 等于 0 时表示退出队列 private long mPtr; // native 层代码,创建 native 层中 NativeMessageQueue private native static long nativeInit(); MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; // 执行 native 层方法 mPtr = nativeInit(); }
MessageQueue的退出
除UI线程外,其他的工作线程都是可以退出的。
// 是否已退出 private boolean mQuitting; // native 方法,退出队列 private native static void nativeDestroy(long ptr); // 退出队列 void quit(boolean safe) { // 如果不是可以手动退出的,抛出异常 if (!mQuitAllowed) { throw new IllegalStateException("Main thread not allowed to quit."); } synchronized (this) { // 如果已经退出了直接结束方法 if (mQuitting) { return; } // 标记为已退出状态 mQuitting = true; if (safe) { //暴力清除掉队列中所有的消息 removeAllFutureMessagesLocked(); } else { //清除掉可能还没有被处理的消息 removeAllMessagesLocked(); } // 注销 nativeWake(mPtr); } }
private void removeAllMessagesLocked() { Message p = mMessages; //从队列头中取消息,全部拿出来回收掉 while (p != null) { Message n = p.next; p.recycleUnchecked(); p = n; } mMessages = null; } private void removeAllFutureMessagesLocked() { // 获取当前系统时间 final long now = SystemClock.uptimeMillis(); Message p = mMessages; if (p != null) { // 判断当前消息对象的预处理时间是否晚于当前时间 if (p.when > now) { // 如果当前消息对象的预处理时间晚于当前时间直接全部暴力清除 removeAllMessagesLocked(); } else { Message n; // 表明此时有可能这个消息正在被分发处理,则跳过该消息往后找晚于当前时间的消息 for (;;) { n = p.next; if (n == null) { return; } if (n.when > now) { // 如果找到了晚于当前时间的消息结束循环 break; } p = n; } p.next = null; do { // n 就是那个晚于当前时间的消息,之后的消息全部回收 p = n; n = p.next; p.recycleUnchecked(); } while (n != null); } } }
消息入队管理
enqueueMessage()方法
// when 就是此消息应该被处理的时间 boolean enqueueMessage(Message msg, long when) { // 如果此消息的 target 也就是宿主 handler 是空的抛异常 if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } // 如果此消息是 in-use 状态抛异常,in-use 的消息不可拿来使用 if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } synchronized (this) { // 若当前 MessageQueue 已经退出了抛异常并释放掉此消息 if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w(TAG, e.getMessage(), e); msg.recycle(); return false; } msg.markInUse();// 将消息标记为 in-use 状态 msg.when = when;// 设置应该被处理的时间 Message p = mMessages; // 拿到队列头 boolean needWake; // 是否需要唤醒线程 /* p == null 队列是空的 when == 0 表示强制把此消息插入队列头部,最先处理 when < p.when 说明此消息应该被处理的时间比队列中第一个要处理的时间还早 */ if (p == null || when == 0 || when < p.when) { msg.next = p; // 将消息插入队列头部 mMessages = msg; needWake = mBlocked; } else { // 线程已经被阻塞 && 消息存在宿主Handler && 消息是异步的 needWake = mBlocked && p.target == null && msg.isAsynchronous(); //如果上述条件都不满足就要按照消息应该被处理的时间插入队列中 Message prev; for (;;) { 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; } if (needWake) { // 判断是否需要唤醒线程 nativeWake(mPtr); } } return true; }
postSyncBarrier()方法
又叫同步消息拦截器,只会影响同步消息。消息拦截器( postSyncBarrier() )与普通消息( enqueueMessage() )的差异在于拦截器的 target 是空的。
private int mNextBarrierToken; // 标识拦截器的token private int postSyncBarrier(long when) { synchronized (this) { final int token = mNextBarrierToken++;// 获取拦截器 token final Message msg = Message.obtain(); msg.markInUse(); // 将对象设置为 in-use 状态 msg.when = when; // 设置时间 msg.arg1 = token; // 将 token 存于消息的常用属性 arg1 中 Message prev = null; Message p = mMessages; // 如果 when 不等于 0 就在队列中按时间找到它的位置 if (when != 0) { while (p != null && p.when <= when) { prev = p; p = p.next; } } // 如果 prev 不等于空就把拦截器插入,否则直接插入队列头部 if (prev != null) { // invariant: p == prev.next msg.next = p; prev.next = msg; } else { msg.next = p; mMessages = msg; } // 拦截器入队成功,返回对应 token return token; } }
token 的作用是找到对应的拦截器删除,同步拦截器可以拦截它之后的所有同步消息,直到这个拦截器被移除。
public void removeSyncBarrier(int token) { synchronized (this) { Message prev = null; Message p = mMessages; // 遍历队列找到指定拦截器 // 查找条件:target 为空,arg1 等于指定 token 值 while (p != null && (p.target != null || p.arg1 != token)) { prev = p; p = p.next; } // 如果 p 等于空说明没找到 if (p == null) { throw new IllegalStateException("The specified message queue synchronization " + " barrier token has not been posted or has already been removed."); } // 是否需要唤醒线程 final boolean needWake; // 在队列中移除掉拦截器 if (prev != null) { prev.next = p.next; // 如果 prev 不等于空说明拦截器前面还有别的消息,就不需要唤醒 needWake = false; } else { mMessages = p.next; // 拦截器在队列头部,移除它之后如果队列空了或者它的下一个消息是个正常消息就需要唤醒 needWake = mMessages == null || mMessages.target != null; } // 回收 p.recycleUnchecked(); // 判断是否需要唤醒 if (needWake && !mQuitting) { nativeWake(mPtr); } } }
每次队列中没有消息而进入的阻塞状态,我们叫它为“空闲状态”。IdleHandler 是为了提高空闲态利用率而设计的,是 MessageQueue 类下的一个子接口,只有一个方法。
public static interface IdleHandler { /** * 当线程的 MessageQueue 等待更多消息时会调用该方法。 * 返回值:true 只执行一次,false 一直执行 */ boolean queueIdle(); }
MessageQueue 为我们提供了添加和删除 IdleHandler 的方法:
//使用一个 ArrayList 存储 IdleHandler private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>(); // 添加一个 IdleHandler public void addIdleHandler(@NonNull IdleHandler handler) { if (handler == null) { throw new NullPointerException("Can't add a null IdleHandler"); } synchronized (this) { mIdleHandlers.add(handler); } } // 删除一个 IdleHandler public void removeIdleHandler(@NonNull IdleHandler handler) { synchronized (this) { mIdleHandlers.remove(handler); } }
消息出队管理
next()方法
private IdleHandler[] mPendingIdleHandlers; Message next() { // mPtr 是从 native 方法中得到的 NativeMessageQueue 地址 final long ptr = mPtr; if (ptr == 0) { //等于0说明队列不存在或被清除掉了 return null; } // 待处理的 IdleHandler 数量,初始化置为-1 int pendingIdleHandlerCount = -1; // 线程将被阻塞的时间 -1:一直阻塞 0:不阻塞 >0:阻塞 nextPollTimeoutMillis 毫秒 int nextPollTimeoutMillis = 0; for (;;) { // 1、nextPollTimeoutMillis 不等于 0 表明需要阻塞线程 if (nextPollTimeoutMillis != 0) { // 为阻塞做准备,释放的需要对象 Binder.flushPendingCommands(); } // 2、阻塞线程操作 nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // 得到当前时间 final long now = SystemClock.uptimeMillis(); Message prevMsg = null; // 3、将待取出消息指针指向队列头。 Message msg = mMessages; // 4、判断队列头是不是同步拦截器 // 如果队列头是同步拦截器的话就将待取出消息指针指向队列头后面最近的一个异步消息。 if (msg != null && msg.target == null) { // 如果是拦截器就向后找一个异步消息 do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } // 5、判断队列是否有可以取出的消息 // 如果消息的待处理时间大于当前时间(now < msg.when)说明当前消息还没到要处理的时间,让线程阻塞到消息待处理的指定时间 // 如果消息的待处理时间小于当前时间(now > msg.when)就直接从队列中 取出消息返回给调用处。(此处会直接结束整个循环,结束 next()方法。 if (msg != null) { if (now < msg.when) { // 如果待取出的消息还没有到应该被处理的时间就让线程阻塞到应该被处理的时间 nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // 直接就能取出消息,所以不用阻塞线程 mBlocked = false; //将消息从队列中剥离出来 if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } // 让消息脱离队列 msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); // 设置为 in-use 状态 msg.markInUse(); // 返回取出的消息,结束循环,结束 next()方法 return msg; } } else { // 队列中没有消息,nextPollTimeoutMillis 等于-1 让线程一直阻塞 nextPollTimeoutMillis = -1; } // 6、若队列已经退出了直接注销和结束方法 if (mQuitting) { dispose(); return null; } // 7、IdleHandler 初始化为-1,所以在本循环中该条件成立的次数<= 1 if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { // 得到 IdleHandler 的数量 pendingIdleHandlerCount = mIdleHandlers.size(); } // 8、pendingIdleHandlerCount 小于或等于 0 说明既没有合适的IdleHandler,结束本次循环 if (pendingIdleHandlerCount <= 0) { // 直接进入下次循环阻塞线程 mBlocked = true; continue; } // 代码执行到此处就说明线程中有待处理的 IdleHandler // 9、初始化 IdleHandler 数组,从IdleHandler集合列表中取出待处理的IdleHandler if (mPendingIdleHandlers == null) { // 初始化待处理 IdleHandler 数组,最小长度为 4 mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } // 从 IdleHandler 集合中获取待处理的 IdleHandler mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // ==========到此处同步代码块已经结束========== for (int i = 0; i < pendingIdleHandlerCount; i++) { // 取出一个 IdleHandler final IdleHandler idler = mPendingIdleHandlers[i]; // 释放掉引用 mPendingIdleHandlers[i] = null; // IdleHandler 的执行模式,true=执行一次,false=总是执行 boolean keep = false; try { // 10、遍历 IdleHandler 数组,执行 IdleHandler 的 queueIdle()代码 keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } // 通过执行模式判断是否需要移除掉对应的 IdleHandler if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } // 11、处理完所有 IdleHandler 将数量置0 pendingIdleHandlerCount = 0; // 12、不阻塞线程,直接去查看有没有新消息(因为执行了 IdleHandler 的代码块,可能已有新的消息入队) nextPollTimeoutMillis = 0; } }