首先,MessageQueue是属于底层类且它依附于创建他的Looper,除Looper外其他类无法单独创建他,如果要使用他,只能从Looper出获得。

下面将从几方面分析:

1. 消息队列存储原理

跟message中obtain一样,通过一个链表缓存池来存储消息,避免重复创建message对象造成额外的消耗。也同样以“sPool”作为缓存池链表的表头,以“next”作为链表的next指针。

2. 使用jni实现native方法

MessageQueue的源码中调用了许多C/C++方法,使用了jni,这些方法所属的底层C/C++创建了属于native层自己的NativeMessageQueue和NativeLooper消息模型。他们对Java层作用就是控制线程是否阻塞。

    // 初始化private native static long nativeInit();// 注销private native static void nativeDestroy(long ptr);// 让线程阻塞指定时长private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/// 立刻唤醒线程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);

3. 创建

创建方法只能由Looper调用。涉及代码:

    // True if the message queue can be quit.private final boolean mQuitAllowed;// 是否可以手动退出/*** native层中NativeMessageQueue队列指针的地址(0——表示退出队列)*/@SuppressWarnings("unused")private long mPtr; // used by native code// native层代码,创建native层的 NativeMessageQueueprivate native static long nativeInit();    // 构造方法MessageQueue(boolean quitAllowed) {mQuitAllowed = quitAllowed;mPtr = nativeInit();// 执行native方法,初始化}

其中,mQuitAllowed 表示当前的MessageQueue是否可以手动退出。

Android中要求UI线程不可手动退出,除此之外,其他的线程都可以手动退出,具体操作在Looper和UI线程中。

4. 销毁或退出

退出就是当前这个MessageQueue停止服务,将队列中已存在的所有消息全部清空。看代码:

    // 是否退出标记private boolean mQuitting;// native退出方法private native static void nativeDestroy(long ptr);void quit(boolean safe) {if (!mQuitAllowed) {// 如果设置Wie不可以手动退出,但调用该方法,抛异常throw new IllegalStateException("Main thread not allowed to quit.");}synchronized (this) {if (mQuitting) {// 如果已经退出,直接结束return;}mQuitting = true;// 改变标记状态if (safe) {// 清除掉可能还没有被处理的message(或未到处理时间的message)removeAllFutureMessagesLocked();} else {// 直接粗暴的清空队列中的所有messageremoveAllMessagesLocked();}// We can assume mPtr != 0 because mQuitting was previously false.nativeWake(mPtr);}}// 直接粗暴的清空队列中的所有messageprivate void removeAllMessagesLocked() {Message p = mMessages;// 直接死循环,全部回收while (p != null) {Message n = p.next;p.recycleUnchecked();p = n;}mMessages = null;}// 清除掉可能还没有被处理的message(或未到处理时间的message)private void removeAllFutureMessagesLocked() {final long now = SystemClock.uptimeMillis();// 当前时间Message p = mMessages;if (p != null) {if (p.when > now) {// 该message执行时间晚于当前时间,即还未达到执行时间removeAllMessagesLocked();// 直接回收} else {Message n;/*** 如果当前消息的预处理时间并不晚于当前时间,说明这个消息可能正在被分发处理,* 则跳过该消息,继续找*/for (;;) {n = p.next;if (n == null) {return;}if (n.when > now) {// 找到了下一个未执行的消息break;}p = n;// 以这个消息为界,之后的消息都大于当前时间,即都是未执行的消息}p.next = null;// 从消息n开始的消息,全部被回收。do {p = n;n = p.next;p.recycleUnchecked();} while (n != null);}}}

其中的逻辑,都在注释中。

5.消息入队方法:enqueueMessage()

说白了就是将消息放入队列,我们知道消息是按其执行顺序排序的,所以肯定会判断when属性。具体代码:

    boolean enqueueMessage(Message msg, long when) {// msg.target即msg所属的handlerif (msg.target == null) {throw new IllegalArgumentException("Message must have a target.");}// 判断该msg是否处于in-use状态(因为in-use状态不能拿来使用)if (msg.isInUse()) {throw new IllegalStateException(msg + " This message is already in use.");}synchronized (this) {if (mQuitting) {// 如果当前的messagequeue是退出状态,则抛异常,并释放该msgIllegalStateException 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();// 将该msg标记为in-use状态msg.when = when;Message p = mMessages;// messagequeue的消息头boolean needWake;// 是否需要唤醒线程/*** 判断插入的依据:* 1. p为空,即当前的messagequeue为空,那直接插入即可* 2. when=0,表示该msg需要立即执行,需要出入队列的头,最先执行* 3. when < p.when,虽然不需要立即执行,但它比最先要执行的消息执行时间还要早,也放入队头*/if (p == null || when == 0 || when < p.when) {// New head, wake up the event queue if blocked.msg.next = p;mMessages = msg;needWake = mBlocked;} else {// Inserted within the middle of the queue.  Usually we don't have to wake// up the event queue unless there is a barrier at the head of the queue// and the message is the earliest asynchronous message in the queue.// 线程已经被阻塞 && 消息所属handler为异步的needWake = mBlocked && p.target == null && msg.isAsynchronous();// 如果以上条件都不满足,就按消息的when属性插入队列中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.nextprev.next = msg;}// 判断是否小唤醒线程// We can assume mPtr != 0 because mQuitting is false.if (needWake) {nativeWake(mPtr);}}return true;}

总结一下消息入队的逻辑大致为:

1.检查消息的合法性:包括其所属handler是否为空、是否是in-use状态、队列是否存活;

2. 如果满足条件:队列为空 或 when=0 或 when<队列头的when,则直接将此消息放到队列头

3.如果以上条件都不满足,则从头遍历队列,根据when将消息放到对应的位置。

6.同步消息拦截器

(同步消息拦截器的本质也是一个message对象,只不过他的“target”为空)

除了enqueueMessage()可以向队列中添加消息外,还有一个postSyncBarrier()方法也可以向队列标价消息,但他添加的不是普通的message对象,整个被添加的特殊的Message就是同步消息拦截器。该拦截器会影响同步消息:消息默认都是同步的(只有设置 了setAsychronous(true)后的消息才是异步消息),但不会影响异步消息,这也是他的作用:拦截队列中的同步消息, 放行异步消息. 就好像交警一样, 在道路拥挤的时候决定哪些车可以先通过, 在这里这些先通过的车辆指的就是异步消息。

涉及代码:

    // The next barrier token.// Barriers are indicated by messages with a null target whose arg1 field carries the token.private int mNextBarrierToken; // 标识拦截器的token(这也是该拦截器的唯一标识)/*** Posts a synchronization barrier to the Looper's message queue.** Message processing occurs as usual until the message queue encounters the* synchronization barrier that has been posted.  When the barrier is encountered,* later synchronous messages in the queue are stalled (prevented from being executed)* until the barrier is released by calling {@link #removeSyncBarrier} and specifying* the token that identifies the synchronization barrier.** This method is used to immediately postpone execution of all subsequently posted* synchronous messages until a condition is met that releases the barrier.* Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier* and continue to be processed as usual.** This call must be always matched by a call to {@link #removeSyncBarrier} with* the same token to ensure that the message queue resumes normal operation.* Otherwise the application will probably hang!** @return A token that uniquely identifies the barrier.  This token must be* passed to {@link #removeSyncBarrier} to release the barrier.** @hide*/public int postSyncBarrier() {return postSyncBarrier(SystemClock.uptimeMillis());}private int postSyncBarrier(long when) {// Enqueue a new sync barrier token.// We don't need to wake the queue because the purpose of a barrier is to stall it.synchronized (this) {final int token = mNextBarrierToken++;// 获得拦截器token/*** 实例化拦截器:状态设置为in-use,token设置到属性args1中*/final Message msg = Message.obtain();msg.markInUse();msg.when = when;msg.arg1 = token;Message prev = null;Message p = mMessages;if (when != 0) {while (p != null && p.when <= when) {prev = p;p = p.next;}}if (prev != null) { // invariant: p == prev.nextmsg.next = p;prev.next = msg;} else {msg.next = p;mMessages = msg;}return token;// 拦截器插入成功,返回token}}/*** 通过token删除拦截器* Removes a synchronization barrier.** @param token The synchronization barrier token that was returned by* {@link #postSyncBarrier}.** @throws IllegalStateException if the barrier was not found.** @hide*/public void removeSyncBarrier(int token) {// Remove a sync barrier token from the queue.// If the queue is no longer stalled by a barrier then wake it.synchronized (this) {Message prev = null;Message p = mMessages;// 遍历队列找拦截器,查找条件为:target为空,args1为指定的tokenwhile (p != null && (p.target != null || p.arg1 != token)) {prev = p;p = p.next;}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 the loop is quitting then it is already awake.// We can assume mPtr != 0 when mQuitting is false.// 再次判断是否需要唤醒if (needWake && !mQuitting) {nativeWake(mPtr);}}}

7.队列空闲处理器:IdleHandler

理解可参考:【Android 异步操作】Handler 机制 ( MessageQueue 空闲任务 IdleHandler 机制 )_韩曙亮的博客-CSDN博客_messagequeue.idlehandler

由于在从队列中取出消息时队里可能是空的,这时候就会阻塞线程等待消息到来,每次队列中没有消息二进入的阻塞状态,就叫“空闲状态”,为了更好的利用资源,也为了更好的掌握线程的状态,于是就有了度低劣空闲处理器——“IdleHandler”。

IdleHandler是一个接口,内部只封装了一个方法:queueIdle();。当一个线程的消息队列为空,或保存在消息队列头部的消息的处理时间大于系统的当前时间时,线程处于一种空闲状态,接下来它会进入睡眠状态。在进入睡眠状态前,线程会发出线程空闲消息给那些注册了的空闲消息处理器来处理。涉及代码:

     private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();    /*** Callback interface for discovering when a thread is going to block* waiting for more messages.*/public static interface IdleHandler {/*** Called when the message queue has run out of messages and will now* wait for more.  Return true to keep your idle handler active, false* to have it removed.  This may be called if there are still messages* pending in the queue, but they are all scheduled to be dispatched* after the current time.*/boolean queueIdle();}/*** Add a new {@link IdleHandler} to this message queue.  This may be* removed automatically for you by returning false from* {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is* invoked, or explicitly removing it with {@link #removeIdleHandler}.** <p>This method is safe to call from any thread.** @param handler The IdleHandler to be added.*/public void addIdleHandler(@NonNull IdleHandler handler) {if (handler == null) {throw new NullPointerException("Can't add a null IdleHandler");}synchronized (this) {mIdleHandlers.add(handler);}}/*** Remove an {@link IdleHandler} from the queue that was previously added* with {@link #addIdleHandler}.  If the given object is not currently* in the idle list, nothing is done.** <p>This method is safe to call from any thread.** @param handler The IdleHandler to be removed.*/public void removeIdleHandler(@NonNull IdleHandler handler) {synchronized (this) {mIdleHandlers.remove(handler);}}

8.消息出队方法:next()

直接上代码吧,看注释:

    Message next() {// Return here if the message loop has already quit and been disposed.// This can happen if the application tries to restart a looper after quit// which is not supported./*** mPtr 是从native方法中得到的NativeMessageQueue地址,如果mPtr=0,说明队列不存在或被清除掉了*/final long ptr = mPtr;if (ptr == 0) {return null;}// 待处理的IdleHandler数量,只有第一次初始化时为-1int pendingIdleHandlerCount = -1; // -1 only during first iteration// 线程被阻塞时间: -1则一直阻塞;0则不阻塞;大于0则为阻塞时长(单位:毫秒)int nextPollTimeoutMillis = 0;for (;;) {if (nextPollTimeoutMillis != 0) {// 如果nextPollTimeoutMillis != 0,说明线程要阻塞了// 为长时间阻塞做准备:把该释放的对象都释放了Binder.flushPendingCommands();}nativePollOnce(ptr, nextPollTimeoutMillis);// 阻塞线程操作synchronized (this) {// Try to retrieve the next message.  Return if found.final long now = SystemClock.uptimeMillis();Message prevMsg = null;Message msg = mMessages;if (msg != null && msg.target == null) {// 是否为同步拦截器(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);} else {// 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();// 置为in-use状态return msg;// 返回消息,结束循环,结束next()方法}} else {// No more messages. 队列中没有可取的消息,则一直阻塞线程nextPollTimeoutMillis = -1;}// Process the quit message now that all pending messages have been handled.if (mQuitting) {// 如果队列已经退出,则直接注销和结束方法dispose();return null;}// If first time idle, then get the number of idlers to run.// Idle handles only run if the queue is empty or if the first message// in the queue (possibly a barrier) is due to be handled in the future.// IdleHandler初始化为:-1,所以在本循环中该条件成立次数<=1if (pendingIdleHandlerCount < 0&& (mMessages == null || now < mMessages.when)) {// 得到IdleHandler的数量pendingIdleHandlerCount = mIdleHandlers.size();}if (pendingIdleHandlerCount <= 0) {// 即没有合适的消息也没有合适的闲时处理// No idle handlers to run.  Loop and wait some more.mBlocked = true;// 直接进入下次循环阻塞线程continue;}// 说明线程中有待处理的IdleHandler,则从IdleHandler集合中取出IdleHandlerif (mPendingIdleHandlers == null) {// 初始化待处理的IdleHandler数组的长度为4mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];}// 从IdleHandler集合中获取待处理的IdleHandlermPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);}
///到此,同步代码块结束// Run the idle handlers.// We only ever reach this code block during the first iteration.for (int i = 0; i < pendingIdleHandlerCount; i++) {// 取出一个IdleHandlerfinal IdleHandler idler = mPendingIdleHandlers[i];// 释放掉引用mPendingIdleHandlers[i] = null; // release the reference to the handler// IdleHandler的执行模式:true为执行一次;false为一直执行boolean keep = false;try {// 获得执行模式keep = idler.queueIdle();} catch (Throwable t) {Log.wtf(TAG, "IdleHandler threw exception", t);}// 通过执行模式,判断是否需要移除掉对应的IdleHandlerif (!keep) {synchronized (this) {mIdleHandlers.remove(idler);}}}// Reset the idle handler count to 0 so we do not run them again.pendingIdleHandlerCount = 0;// 处理完所有的IdleHandler,将数量置为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.// 因为执行了IdleHandler的代码块,有可能已经有新的消息入队列了,所有这里不阻塞线程,直接去查看是否有新的消息nextPollTimeoutMillis = 0;}}

分析一下核心代码for循环里的逻辑:

1. 根据 nextPollTimeoutMillis 判断是否阻塞线程,初始值为0,不阻塞线程。

2. 将“待取出消息指针”指向队列头。

3. 如果队列头是同步消息拦截器的话,就将“待取出消息指针”指向队列的第一个异步消息。

4. 如果“待取出消息指针”不可用,即msg==null,说明队列中没有可取出的消息,让 nextPollTimeoutMillis = -1,阻塞线程,等待消息到来唤醒它。

5. 如果“待取出消息指针”可用,那再判断消息的待处理时间:

如果消息的待处理时间>当前时间,那让线程阻塞到其执行时间;

如果消息的待处理时间<=当前时间,直接取出返回给调用的地方(此处会直接结束整个循环,结束next()方法)。

6. 如果队列已经退出,那就直接结束next方法

7. 如果是第一次死循环就初始化IdleHandler数量的局部变量:pendingIdleHandlerCount

8. 如果IdleHandler数量<=0,说明没有合适的IdleHandler,直接进入下一次循环阻塞线程(此处会直接结束本次循环)。

9. 初始化IdleHandler数组,里面保存这本地待处理的IdleHandler

10. 遍历IdleHandler数组,执行对应的queueIdle()方法。

11. 执行完所有的IdleHandler之后,将IdleHandler数据清0。

12. 因为执行了IdleHandler的代码块,有可能已经有新的消息入队列了,所有这里不阻塞线程,直接去查看是否有新的消息。

13. 本次循环结束,进行下一次循环。

总结:MessageQueue队列消息是有序的(按消息待处理时间排序);同步拦截器可以拦截他之后的所有同步消息,知道这个拦截器被移除;取出消息时如果没有合适的消息线程会阻塞。

下一篇讲Looper源码

最后附上MessageQueue源码:

/** Copyright (C) 2006 The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package android.os;import android.annotation.IntDef;
import android.annotation.NonNull;
import android.os.MessageQueueProto;
import android.util.Log;
import android.util.Printer;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;import java.io.FileDescriptor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;/*** Low-level class holding the list of messages to be dispatched by a* {@link Looper}.  Messages are not added directly to a MessageQueue,* but rather through {@link Handler} objects associated with the Looper.** <p>You can retrieve the MessageQueue for the current thread with* {@link Looper#myQueue() Looper.myQueue()}.*/
public final class MessageQueue {private static final String TAG = "MessageQueue";private static final boolean DEBUG = false;// True if the message queue can be quit.private final boolean mQuitAllowed;@SuppressWarnings("unused")private long mPtr; // used by native codeMessage mMessages;private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;private IdleHandler[] mPendingIdleHandlers;private boolean mQuitting;// Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.private boolean mBlocked;// The next barrier token.// Barriers are indicated by messages with a null target whose arg1 field carries the token.private int mNextBarrierToken;private native static long nativeInit();private native static void nativeDestroy(long ptr);private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/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(boolean quitAllowed) {mQuitAllowed = quitAllowed;mPtr = nativeInit();}@Overrideprotected void finalize() throws Throwable {try {dispose();} finally {super.finalize();}}// Disposes of the underlying message queue.// Must only be called on the looper thread or the finalizer.private void dispose() {if (mPtr != 0) {nativeDestroy(mPtr);mPtr = 0;}}/*** Returns true if the looper has no pending messages which are due to be processed.** <p>This method is safe to call from any thread.** @return True if the looper is idle.*/public boolean isIdle() {synchronized (this) {final long now = SystemClock.uptimeMillis();return mMessages == null || now < mMessages.when;}}/*** Add a new {@link IdleHandler} to this message queue.  This may be* removed automatically for you by returning false from* {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is* invoked, or explicitly removing it with {@link #removeIdleHandler}.** <p>This method is safe to call from any thread.** @param handler The IdleHandler to be added.*/public void addIdleHandler(@NonNull IdleHandler handler) {if (handler == null) {throw new NullPointerException("Can't add a null IdleHandler");}synchronized (this) {mIdleHandlers.add(handler);}}/*** Remove an {@link IdleHandler} from the queue that was previously added* with {@link #addIdleHandler}.  If the given object is not currently* in the idle list, nothing is done.** <p>This method is safe to call from any thread.** @param handler The IdleHandler to be removed.*/public void removeIdleHandler(@NonNull IdleHandler handler) {synchronized (this) {mIdleHandlers.remove(handler);}}/*** Returns whether this looper's thread is currently polling for more work to do.* This is a good signal that the loop is still alive rather than being stuck* handling a callback.  Note that this method is intrinsically racy, since the* state of the loop can change before you get the result back.** <p>This method is safe to call from any thread.** @return True if the looper is currently polling for events.* @hide*/public boolean isPolling() {synchronized (this) {return isPollingLocked();}}private boolean isPollingLocked() {// If the loop is quitting then it must not be idling.// We can assume mPtr != 0 when mQuitting is false.return !mQuitting && nativeIsPolling(mPtr);}/*** Adds a file descriptor listener to receive notification when file descriptor* related events occur.* <p>* If the file descriptor has already been registered, the specified events* and listener will replace any that were previously associated with it.* It is not possible to set more than one listener per file descriptor.* </p><p>* It is important to always unregister the listener when the file descriptor* is no longer of use.* </p>** @param fd The file descriptor for which a listener will be registered.* @param events The set of events to receive: a combination of the* {@link OnFileDescriptorEventListener#EVENT_INPUT},* {@link OnFileDescriptorEventListener#EVENT_OUTPUT}, and* {@link OnFileDescriptorEventListener#EVENT_ERROR} event masks.  If the requested* set of events is zero, then the listener is unregistered.* @param listener The listener to invoke when file descriptor events occur.** @see OnFileDescriptorEventListener* @see #removeOnFileDescriptorEventListener*/public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd,@OnFileDescriptorEventListener.Events int events,@NonNull OnFileDescriptorEventListener listener) {if (fd == null) {throw new IllegalArgumentException("fd must not be null");}if (listener == null) {throw new IllegalArgumentException("listener must not be null");}synchronized (this) {updateOnFileDescriptorEventListenerLocked(fd, events, listener);}}/*** Removes a file descriptor listener.* <p>* This method does nothing if no listener has been registered for the* specified file descriptor.* </p>** @param fd The file descriptor whose listener will be unregistered.** @see OnFileDescriptorEventListener* @see #addOnFileDescriptorEventListener*/public void removeOnFileDescriptorEventListener(@NonNull FileDescriptor fd) {if (fd == null) {throw new IllegalArgumentException("fd must not be null");}synchronized (this) {updateOnFileDescriptorEventListenerLocked(fd, 0, null);}}private void updateOnFileDescriptorEventListenerLocked(FileDescriptor fd, int events,OnFileDescriptorEventListener listener) {final int fdNum = fd.getInt$();int index = -1;FileDescriptorRecord record = null;if (mFileDescriptorRecords != null) {index = mFileDescriptorRecords.indexOfKey(fdNum);if (index >= 0) {record = mFileDescriptorRecords.valueAt(index);if (record != null && record.mEvents == events) {return;}}}if (events != 0) {events |= OnFileDescriptorEventListener.EVENT_ERROR;if (record == null) {if (mFileDescriptorRecords == null) {mFileDescriptorRecords = new SparseArray<FileDescriptorRecord>();}record = new FileDescriptorRecord(fd, events, listener);mFileDescriptorRecords.put(fdNum, record);} else {record.mListener = listener;record.mEvents = events;record.mSeq += 1;}nativeSetFileDescriptorEvents(mPtr, fdNum, events);} else if (record != null) {record.mEvents = 0;mFileDescriptorRecords.removeAt(index);nativeSetFileDescriptorEvents(mPtr, fdNum, 0);}}// Called from native code.private int dispatchEvents(int fd, int events) {// Get the file descriptor record and any state that might change.final FileDescriptorRecord record;final int oldWatchedEvents;final OnFileDescriptorEventListener listener;final int seq;synchronized (this) {record = mFileDescriptorRecords.get(fd);if (record == null) {return 0; // spurious, no listener registered}oldWatchedEvents = record.mEvents;events &= oldWatchedEvents; // filter events based on current watched setif (events == 0) {return oldWatchedEvents; // spurious, watched events changed}listener = record.mListener;seq = record.mSeq;}// Invoke the listener outside of the lock.int newWatchedEvents = listener.onFileDescriptorEvents(record.mDescriptor, events);if (newWatchedEvents != 0) {newWatchedEvents |= OnFileDescriptorEventListener.EVENT_ERROR;}// Update the file descriptor record if the listener changed the set of// events to watch and the listener itself hasn't been updated since.if (newWatchedEvents != oldWatchedEvents) {synchronized (this) {int index = mFileDescriptorRecords.indexOfKey(fd);if (index >= 0 && mFileDescriptorRecords.valueAt(index) == record&& record.mSeq == seq) {record.mEvents = newWatchedEvents;if (newWatchedEvents == 0) {mFileDescriptorRecords.removeAt(index);}}}}// Return the new set of events to watch for native code to take care of.return newWatchedEvents;}Message next() {// Return here if the message loop has already quit and been disposed.// This can happen if the application tries to restart a looper after quit// which is not supported.final long ptr = mPtr;if (ptr == 0) {return null;}int pendingIdleHandlerCount = -1; // -1 only during first iterationint nextPollTimeoutMillis = 0;for (;;) {if (nextPollTimeoutMillis != 0) {Binder.flushPendingCommands();}nativePollOnce(ptr, nextPollTimeoutMillis);synchronized (this) {// Try to retrieve the next message.  Return if found.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);} else {// 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) {dispose();return null;}// If first time idle, then get the number of idlers to run.// Idle handles only run if the queue is empty or if the first message// in the queue (possibly a barrier) is due to be handled in the future.if (pendingIdleHandlerCount < 0&& (mMessages == null || now < mMessages.when)) {pendingIdleHandlerCount = mIdleHandlers.size();}if (pendingIdleHandlerCount <= 0) {// No idle handlers to run.  Loop and wait some more.mBlocked = true;continue;}if (mPendingIdleHandlers == null) {mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];}mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);}// Run the idle handlers.// We only ever reach this code block during the first iteration.for (int i = 0; i < pendingIdleHandlerCount; i++) {final IdleHandler idler = mPendingIdleHandlers[i];mPendingIdleHandlers[i] = null; // release the reference to the handlerboolean keep = false;try {keep = idler.queueIdle();} catch (Throwable t) {Log.wtf(TAG, "IdleHandler threw exception", t);}if (!keep) {synchronized (this) {mIdleHandlers.remove(idler);}}}// 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;}}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();}// We can assume mPtr != 0 because mQuitting was previously false.nativeWake(mPtr);}}/*** Posts a synchronization barrier to the Looper's message queue.** Message processing occurs as usual until the message queue encounters the* synchronization barrier that has been posted.  When the barrier is encountered,* later synchronous messages in the queue are stalled (prevented from being executed)* until the barrier is released by calling {@link #removeSyncBarrier} and specifying* the token that identifies the synchronization barrier.** This method is used to immediately postpone execution of all subsequently posted* synchronous messages until a condition is met that releases the barrier.* Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier* and continue to be processed as usual.** This call must be always matched by a call to {@link #removeSyncBarrier} with* the same token to ensure that the message queue resumes normal operation.* Otherwise the application will probably hang!** @return A token that uniquely identifies the barrier.  This token must be* passed to {@link #removeSyncBarrier} to release the barrier.** @hide*/public int postSyncBarrier() {return postSyncBarrier(SystemClock.uptimeMillis());}private int postSyncBarrier(long when) {// Enqueue a new sync barrier token.// We don't need to wake the queue because the purpose of a barrier is to stall it.synchronized (this) {final int token = mNextBarrierToken++;final Message msg = Message.obtain();msg.markInUse();msg.when = when;msg.arg1 = token;Message prev = null;Message p = mMessages;if (when != 0) {while (p != null && p.when <= when) {prev = p;p = p.next;}}if (prev != null) { // invariant: p == prev.nextmsg.next = p;prev.next = msg;} else {msg.next = p;mMessages = msg;}return token;}}/*** Removes a synchronization barrier.** @param token The synchronization barrier token that was returned by* {@link #postSyncBarrier}.** @throws IllegalStateException if the barrier was not found.** @hide*/public void removeSyncBarrier(int token) {// Remove a sync barrier token from the queue.// If the queue is no longer stalled by a barrier then wake it.synchronized (this) {Message prev = null;Message p = mMessages;while (p != null && (p.target != null || p.arg1 != token)) {prev = p;p = p.next;}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;needWake = false;} else {mMessages = p.next;needWake = mMessages == null || mMessages.target != null;}p.recycleUnchecked();// If the loop is quitting then it is already awake.// We can assume mPtr != 0 when mQuitting is false.if (needWake && !mQuitting) {nativeWake(mPtr);}}}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) {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();msg.when = when;Message p = mMessages;boolean needWake;if (p == null || when == 0 || when < p.when) {// New head, wake up the event queue if blocked.msg.next = p;mMessages = msg;needWake = mBlocked;} else {// Inserted within the middle of the queue.  Usually we don't have to wake// up the event queue unless there is a barrier at the head of the queue// and the message is the earliest asynchronous message in the queue.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.nextprev.next = msg;}// We can assume mPtr != 0 because mQuitting is false.if (needWake) {nativeWake(mPtr);}}return true;}boolean hasMessages(Handler h, int what, Object object) {if (h == null) {return false;}synchronized (this) {Message p = mMessages;while (p != null) {if (p.target == h && p.what == what && (object == null || p.obj == object)) {return true;}p = p.next;}return false;}}boolean hasMessages(Handler h, Runnable r, Object object) {if (h == null) {return false;}synchronized (this) {Message p = mMessages;while (p != null) {if (p.target == h && p.callback == r && (object == null || p.obj == object)) {return true;}p = p.next;}return false;}}boolean hasMessages(Handler h) {if (h == null) {return false;}synchronized (this) {Message p = mMessages;while (p != null) {if (p.target == h) {return true;}p = p.next;}return false;}}void removeMessages(Handler h, int what, Object object) {if (h == null) {return;}synchronized (this) {Message p = mMessages;// Remove all messages at front.while (p != null && p.target == h && p.what == what&& (object == null || p.obj == object)) {Message n = p.next;mMessages = n;p.recycleUnchecked();p = n;}// Remove all messages after front.while (p != null) {Message n = p.next;if (n != null) {if (n.target == h && n.what == what&& (object == null || n.obj == object)) {Message nn = n.next;n.recycleUnchecked();p.next = nn;continue;}}p = n;}}}void removeMessages(Handler h, Runnable r, Object object) {if (h == null || r == null) {return;}synchronized (this) {Message p = mMessages;// Remove all messages at front.while (p != null && p.target == h && p.callback == r&& (object == null || p.obj == object)) {Message n = p.next;mMessages = n;p.recycleUnchecked();p = n;}// Remove all messages after front.while (p != null) {Message n = p.next;if (n != null) {if (n.target == h && n.callback == r&& (object == null || n.obj == object)) {Message nn = n.next;n.recycleUnchecked();p.next = nn;continue;}}p = n;}}}void removeCallbacksAndMessages(Handler h, Object object) {if (h == null) {return;}synchronized (this) {Message p = mMessages;// Remove all messages at front.while (p != null && p.target == h&& (object == null || p.obj == object)) {Message n = p.next;mMessages = n;p.recycleUnchecked();p = n;}// Remove all messages after front.while (p != null) {Message n = p.next;if (n != null) {if (n.target == h && (object == null || n.obj == object)) {Message nn = n.next;n.recycleUnchecked();p.next = nn;continue;}}p = n;}}}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 {p = n;n = p.next;p.recycleUnchecked();} while (n != null);}}}void dump(Printer pw, String prefix, Handler h) {synchronized (this) {long now = SystemClock.uptimeMillis();int n = 0;for (Message msg = mMessages; msg != null; msg = msg.next) {if (h == null || h == msg.target) {pw.println(prefix + "Message " + n + ": " + msg.toString(now));}n++;}pw.println(prefix + "(Total messages: " + n + ", polling=" + isPollingLocked()+ ", quitting=" + mQuitting + ")");}}void writeToProto(ProtoOutputStream proto, long fieldId) {final long messageQueueToken = proto.start(fieldId);synchronized (this) {for (Message msg = mMessages; msg != null; msg = msg.next) {msg.writeToProto(proto, MessageQueueProto.MESSAGES);}proto.write(MessageQueueProto.IS_POLLING_LOCKED, isPollingLocked());proto.write(MessageQueueProto.IS_QUITTING, mQuitting);}proto.end(messageQueueToken);}/*** Callback interface for discovering when a thread is going to block* waiting for more messages.*/public static interface IdleHandler {/*** Called when the message queue has run out of messages and will now* wait for more.  Return true to keep your idle handler active, false* to have it removed.  This may be called if there are still messages* pending in the queue, but they are all scheduled to be dispatched* after the current time.*/boolean queueIdle();}/*** A listener which is invoked when file descriptor related events occur.*/public interface OnFileDescriptorEventListener {/*** File descriptor event: Indicates that the file descriptor is ready for input* operations, such as reading.* <p>* The listener should read all available data from the file descriptor* then return <code>true</code> to keep the listener active or <code>false</code>* to remove the listener.* </p><p>* In the case of a socket, this event may be generated to indicate* that there is at least one incoming connection that the listener* should accept.* </p><p>* This event will only be generated if the {@link #EVENT_INPUT} event mask was* specified when the listener was added.* </p>*/public static final int EVENT_INPUT = 1 << 0;/*** File descriptor event: Indicates that the file descriptor is ready for output* operations, such as writing.* <p>* The listener should write as much data as it needs.  If it could not* write everything at once, then it should return <code>true</code> to* keep the listener active.  Otherwise, it should return <code>false</code>* to remove the listener then re-register it later when it needs to write* something else.* </p><p>* This event will only be generated if the {@link #EVENT_OUTPUT} event mask was* specified when the listener was added.* </p>*/public static final int EVENT_OUTPUT = 1 << 1;/*** File descriptor event: Indicates that the file descriptor encountered a* fatal error.* <p>* File descriptor errors can occur for various reasons.  One common error* is when the remote peer of a socket or pipe closes its end of the connection.* </p><p>* This event may be generated at any time regardless of whether the* {@link #EVENT_ERROR} event mask was specified when the listener was added.* </p>*/public static final int EVENT_ERROR = 1 << 2;/** @hide */@Retention(RetentionPolicy.SOURCE)@IntDef(flag = true, prefix = { "EVENT_" }, value = {EVENT_INPUT,EVENT_OUTPUT,EVENT_ERROR})public @interface Events {}/*** Called when a file descriptor receives events.** @param fd The file descriptor.* @param events The set of events that occurred: a combination of the* {@link #EVENT_INPUT}, {@link #EVENT_OUTPUT}, and {@link #EVENT_ERROR} event masks.* @return The new set of events to watch, or 0 to unregister the listener.** @see #EVENT_INPUT* @see #EVENT_OUTPUT* @see #EVENT_ERROR*/@Events int onFileDescriptorEvents(@NonNull FileDescriptor fd, @Events int events);}private static final class FileDescriptorRecord {public final FileDescriptor mDescriptor;public int mEvents;public OnFileDescriptorEventListener mListener;public int mSeq;public FileDescriptorRecord(FileDescriptor descriptor,int events, OnFileDescriptorEventListener listener) {mDescriptor = descriptor;mEvents = events;mListener = listener;}}
}

安卓 Handler 消息机制之MessageQueue源码相关推荐

  1. 安卓 Handler 消息机制之Message源码

    一 概述 1. Message是handler机制中消息传递的载体,主要用来规范化传输数据的格式. 2. 源码内容含几个部分: 2.1 操作数据相关:一些属性和操作属性的getter和setter方法 ...

  2. Android 系统(177)---Android消息机制分析:Handler、Looper、MessageQueue源码分析

    Android消息机制分析:Handler.Looper.MessageQueue源码分析 1.前言 关于Handler消息机制的博客实际上是非常多的了. 之前也是看别人的博客过来的,但是过了一段时间 ...

  3. 安卓 Handler 消息机制(总)

    注:本文参考于某公开课,如有侵权,请联系本人,会立即删除 注:仅用作自我学习记录,未有任何商业用途. 关于handler的由来 开始前的闲言碎语 我们都知道handler是安卓的消息传递机制,使用ha ...

  4. Handler、Looper与MessageQueue源码分析

    在Android中可以通过Handler来更新主线程中UI的变化,更新UI只能在主线程中进行更新,而为了让其他线程也能控制UI的变化,Android提供了一种机制Handler.Looper与Mess ...

  5. Android Handler消息机制源码分析

    一,前言 众多周知, Android 只允许在主线程中更新UI,因此主线程也称为UI线程(ActivityThread). 如此设计原因有二: (1) 由于UI操作的方法都不是线程安全的,如果多个线程 ...

  6. Android Handler消息机制源码解析

    好记性不如烂笔头,今天来分析一下Handler的源码实现 Handler机制是Android系统的基础,是多线程之间切换的基础.下面我们分析一下Handler的源码实现. Handler消息机制有4个 ...

  7. Android进阶知识树——Android Handler消息机制

    1.概述 在安卓程序启动时,会默认在主线程中 运行程序,那如果执行一些耗时的操作则UI就会处于阻塞状态,出现界面卡顿的现象,再者用户的多种操作,系统是如何做到一一处理的,系统又是如何管理这些任务的,答 ...

  8. Handler消息机制介绍,流程梳理

    文章目录 前言 基本使用 发送和处理Runnable 发送和处理Message 流程梳理 获取消息 发送消息 消息入队 消息出队 理解 源码分析 前言 Handler 是Android 的消息处理机制 ...

  9. Android Handler消息机制不完全解析

    1.Handler的作用 Android开发中,我们经常使用Handler进行页面的更新.例如我们需要在一个下载任务完成后,去更新我们的UI效果,因为AndroidUI操作不是线程安全的,也就意味着我 ...

最新文章

  1. 内存地址 哪个程序_记一次排查线上程序内存的忽高忽低,又是大集合惹祸了...
  2. 计算机组成原理 — CPU 中央处理器
  3. springboot系列八、springboot整合kafka
  4. TCP为什么要三次握手和四次挥手
  5. linux恢复rm删除文件ssd,科学网—linux恢复rm -rf删除的文件 - 罗晓光的博文
  6. 递归大总结之台阶问题
  7. ASP.NET的七个内置对象
  8. 刘强东震怒:疑不满内部拉帮结派人浮于事!
  9. python散点图数据怎么输入_python 散点图添加标签
  10. 网络工程师干货:华为设备故障管理命令大全
  11. 思科路由器交换机指示灯状态详解
  12. 记录建hbase对应hive外表坑
  13. 测试点先发散后收敛思考
  14. 论文阅读报告:Feature Selection for Multi-label Classification Using Neighborhood Preservation,Zhiling Cai
  15. python之meshgrid的使用
  16. 根文件系统制作一制作根文件系统树
  17. 【原】Coursera—Andrew Ng机器学习—课程笔记 Lecture 5 Octave Tutorial
  18. vertical-align属性的用法
  19. mysql一段时间过后 无法连接_MYSQL连接一段时间不操作后出现异常的解决方案
  20. 人人网--互联网巨轮的没落

热门文章

  1. 用 Smali 手写一个可运行的 HelloWorld!!!
  2. 【持续更新中...】抖音火山快手视频去水印小程序
  3. 支付宝沙箱环境接口使用详解
  4. LeetCode刷题记录---腾讯精选练习 50 题
  5. 创客工具机床 桌面式数控机床
  6. 西门子smartclient怎么用_基于Snap7使用C#编程访问西门子PLC系列教程(3)-Snap7Client(建立连接)...
  7. 【创建微服务】创建微服务并使用人人开源代码生成器生成基本代码
  8. 【已解决】Html Webpack Plugin Error;html-withimg-loader无法正常解析html内的img
  9. 深入理解操作系统(12)第四章:处理器体系结构(4)Y86-64的流水线实现(包括:PIPE-处理器/预测下一个PC/分支预测/流水线冒险/暂停,转发避免冒险/PPE硬件结构及实现/CPI)
  10. layui from.render什么意思