Handler
Handler定义
-
Handler本质上是线程间的一个消息传递和处理的机制
-
Handler通信实现的方案本质上是内存共享的方案
- 同一个进程内存是共享的
-
Handler是整个app通信的框架,在ActivityThread里面感受到,整个App都是用它来进行线程间的协调
-
那么以上的这些结论是怎么得来的呢?
Handler流程
-
我们通常使用Handler的方式
class Test { Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { ..... } } Thread thread = new Thread(new Runnable() { @Override public void run() { mHandler.sendEmptyMessage(msg); } }); }
如上面代码示例,在我们使用Handler的时候,我们只是调用了
sendEmptyMessage()
,并实现了handlerMessage()
,但是这个消息是如何从发送端传递到处理端的呢?这之间的调用链到底是怎么样的呢?
Hanlder调用链
-
我们首先从send开始跟踪流程
1. Handler.enqueueMessage()
-
Handler中一共有这么多的消息发送方式,逐一查看后发现除了
sendMessageAtFrontOfQueue()
方法,其他的send/post都是通过sendMessageAtTime()
方法去调用enqueueMessage()
方法,但是即便是sendMessageAtFrontOfQueue()
方法,它最终调用也同样是enqueueMessage()
方法,而纵观所有的发送函数,send和post最大的区别就是:- send方法传递的普遍是message对象
- post方法传递的普遍是Runable对象,而其实在post内部,又会将这个传入的Runable对象封装成Message对象
而之所以这么做,其实都是为了对象的复用,详细的说明可见后续关于消息创建的段落。
简单陈述完相关发送函数的区别之后,接下来就深入探究整个消息发送的调用链,从
sendMessageAtTime()
函数开始:final MessageQueue mQueue; mQueue = looper.mQueue; public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis); } private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) { //target=this,即确认了对应消息的Handler归属,该消息属于哪个Handler msg.target = this; msg.workSourceUid = ThreadLocalWorkSource.getUid(); if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
最终是调用到了MessageQueue的
enqueueMessage()
方法
2. MessageQueue.enqueueMessage()
-
其实到此处,就是将消息插入到MessageQueue中了,那么一个消息的入队是按照何种规则来的呢?
boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } //加锁,锁的是MessageQueue对象 synchronized (this) { //判断消息是否正在被使用,如果正在被使用,则抛出异常 if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } //判断退出标志,如果命中if,则代表着这个MessageQueue已经是退出状态了,不能再往其中插入消息了 if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w(TAG, e.getMessage(), e); //清除消息,直接返回false msg.recycle(); return false; } //将消息InUse标志置位 msg.markInUse(); //消息的执行时间 msg.when = when; //拿到当前消息队列的队列头消息 Message p = mMessages; boolean needWake; /* 判断 ** 1.如果队列为空 ** 2.传入消息是需要立刻执行 ** 3.当前队列头消息的执行时间要晚于传入的消息 ** 那么就将传入的消息排列在队列头*/ if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; //如果当前队列是Blocked的,那么就需要被唤醒 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. //此处涉及同步屏障消息的处理,考虑同步屏障消息的Wake标志位 needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; //进入死循环,遍历整个队列 for (;;) { prev = p; p = p.next; /* beark条件: ** 1.直到队列遍历完毕 ** 2.找到某个消息的执行时间要晚于传入消息 ** 则返回*/ if (p == null || when < p.when) { break; } //同步屏障相关的Wake标志位的判断 if (needWake && p.isAsynchronous()) { needWake = false; } } //将传入的消息放在按执行时间排序的合适的位置上 msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. //如果队列需要被唤醒,即队列当前是阻塞的,那么就唤醒队列 if (needWake) { nativeWake(mPtr); } } return true; }
分析了消息队列中的enqueueMessage()方法,我们能够得知,Handler中的消息队列MessageQueue是按照消息的执行时间进行排序的;上面还涉及同步屏障的判断,这个我们放到后面再去分析。
-
至此,我们的消息就完成了入队的流程。完成了入队,那么消息是在何处出队,又是怎么传到**handleMessage()**中去的呢?
3. Looper.loop()
-
我们的主线程的Looper都是在 ActivityThread.java中的
main()
方法中进行创建的,且在该方法的最后,执行了Looper.loop()
方法/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ public static void loop() { //首先拿到Looper本身 final Looper me = myLooper(); //如果当前并无Looper,则抛出异常 if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } //如果重复调用loop,会造成队列中的消息被重复执行 if (me.mInLoop) { Slog.w(TAG, "Loop again would have the queued messages be executed" + " before this one completed."); } //loop是否被调用的标志 me.mInLoop = true; //获取Looper中的MessageQueue final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. //判断当前线程的身份 Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); // Allow overriding a threshold with a system prop. e.g. // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start' final int thresholdOverride = SystemProperties.getInt("log.looper." + Process.myUid() + "." + Thread.currentThread().getName() + ".slow", 0); boolean slowDeliveryDetected = false; //进入for的死循环 for (;;) { //通过MessageQueue.next()方法进行消息的获取 Message msg = queue.next(); // might block //当拿出来的消息为空时,则退出loop死循环 if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } // Make sure the observer won't change while processing a transaction. final Observer observer = sObserver; final long traceTag = me.mTraceTag; long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs; long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs; if (thresholdOverride > 0) { slowDispatchThresholdMs = thresholdOverride; slowDeliveryThresholdMs = thresholdOverride; } final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0); final boolean logSlowDispatch = (slowDispatchThresholdMs > 0); final boolean needStartTime = logSlowDelivery || logSlowDispatch; final boolean needEndTime = logSlowDispatch; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0; final long dispatchEnd; Object token = null; //如果observer不为空,则获取observer.messageDispatchStarting()的返回值 if (observer != null) { token = observer.messageDispatchStarting(); } long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid); try { //根据从MessageQueue中拿到的消息,调用对应消息的target,即对应的处理消息的Handler的dispatchMessage()方法 msg.target.dispatchMessage(msg); //如果observer不为空,则将当前消息和token传入observer.messageDispatched()方法执行 if (observer != null) { observer.messageDispatched(token, msg); } dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } catch (Exception exception) { if (observer != null) { observer.dispatchingThrewException(token, msg, exception); } throw exception; } finally { ThreadLocalWorkSource.restore(origWorkSource); if (traceTag != 0) { Trace.traceEnd(traceTag); } } if (logSlowDelivery) { if (slowDeliveryDetected) { if ((dispatchStart - msg.when) <= 10) { Slog.w(TAG, "Drained"); slowDeliveryDetected = false; } } else { if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery", msg)) { // Once we write a slow delivery log, suppress until the queue drains. slowDeliveryDetected = true; } } } if (logSlowDispatch) { showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg); } if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } //最终当消息执行完毕,通过recycleUnchecked()将Message进行回收 msg.recycleUnchecked(); } }
通过分析
loop()
方法,我们可以看到,其实是死循环中通过MessageQueue.next()
方法进行消息的获取,那么接下来,我们就来看看next()
方法
4. MessageQueue.next()
-
在
loop()
的死循环中,我们是通过MessageQueue.next()
去获取消息@UnsupportedAppUsage 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; //当MessageQueue已经退出时,再调用next获取消息,会直接返回null if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } //阻塞,一共有三种情况 /** * 1. nextPollTimeoutMillis = -1,永久阻塞,知道收到nativeWake() * 2. nextPollTimeoutMillis = 0, 不阻塞 * 3. nextPollTimeoutMillis > 0, 阻塞nextPollTimeoutMillis时间后再执行 */ nativePollOnce(ptr, nextPollTimeoutMillis); //加锁,锁住MessageQueue对象 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) { //且当前消息执行时间大于当前系统时间,说明还没到执行时间,那么就计算出执行时间和当前时间的差值,赋予nextPollTimeoutMillis,返回阻塞 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; //如果prevMsg不为空,是同步屏障场景,先不做说明 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,永久等待,直到有nativeWake()消息将他唤醒 nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. //如果退出标志位为true,那么直接返回null if (mQuitting) { //销毁mPtr,并将mPtr=0 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. //第一次进来Count=-1,当队列为空,或者未到执行消息时间,获取mIdleHandlers的大小 if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } //如果Count<=0,将阻塞标志置为true,并直接执行下一次循环 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. //下面就是去跑idle handlers,并不需要太关注 for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean 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; } }
分析完
next()
获取消息的方法,我们知道了,MessageQueue队列是通过nativePollOnce()
进行阻塞的,当队列中不存在消息时,会一直阻塞,直到通过MessageQueue.enqueueMessage()
进行消息插入后,会调用nativeWake()
方法来唤醒next()
方法进行消息获取。
5. Handler.dispatchMessage()
-
分析完了消息的获取,接下来继续往下走,看看消息是怎么通过
dispatchMessage()
方法进行分发的/** * Handle system messages here. */ public void dispatchMessage(@NonNull Message msg) { //如果消息本身实现了callback,那么就去调用handleCallback() if (msg.callback != null) { handleCallback(msg); } else { //如果Handler中的mCallback不为空,则调用mCallback的handleMessage() if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } //如果不满足以上两个条件,就调用handleMessage() handleMessage(msg); } } private static void handleCallback(Message message) { message.callback.run(); } /** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. */ public interface Callback { /** * @param msg A {@link android.os.Message Message} object * @return True if no further handling is desired */ boolean handleMessage(@NonNull Message msg); } /** * Subclasses must implement this to receive messages. */ public void handleMessage(@NonNull Message msg) {}
可以看到,调用
dispatchMessage()
就将消息分发了出去,而我们只需要在创建Handler的时候,实现对应的handle处理方法即可。 -
至此,我们就分析完了整个Handler消息传递和分发的流程了。接下来让我们来看一下Handler的创建
Handler的创建
-
上面我们分析了Handler使用时消息传递和分发的整个流程,在消息传递的过程中,我们发现涉及了Looper、MessageQueue这两个类,那么这两个类是什么,又是在何处被初始化的呢?
-
首先我们来看一下Handler中维护了哪些成员变量,分别起什么作用
@UnsupportedAppUsage final Looper mLooper; //持有的Looper变量 final MessageQueue mQueue; //持有的MessageQueue变量 @UnsupportedAppUsage final Callback mCallback; //持有的Callback变量 final boolean mAsynchronous; //标志位,判断是否对传入的消息置异步消息标志 @UnsupportedAppUsage IMessenger mMessenger;
我们可以看到,Handler内部持有了这些成员变量,接下来我们去看Handler初始化
public Handler(@Nullable Callback callback, boolean async) { //检查是否存在潜在的内存泄露 if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } //Looper变量的赋值 mLooper = Looper.myLooper(); //如果获取出来为空,直接抛异常 if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()"); } //MessageQueue赋值,Handler中持有的Queue,其实是Looper中的Queue mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
Handler中的Looper
-
可以看到,Handler中的Looper变量并非新创建的,而是通过
Looper.mylooper()
方法获取的,而且Handler中的MessageQueue也不是新创建的,而是直接持有的Looper中的Queuepublic static @Nullable Looper myLooper() { return sThreadLocal.get(); }
可以看到,
myLooper()
方法也并不是在创建Looper,而是直接从ThreadLocal变量中获取,而ThreadLocal是和线程绑定的,每一个线程都会有单独的一份ThreadLocal,那么这也就意味着,每一个线程只有一份Looper。查看Looper的构造函数private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
Looper类的构造函数是私有的,即无法在外部直接调用构造函数进行Looper的创建,那么我们该如何创建Looper呢?分两种情况:
-
当前线程是主线程,那么系统会主动调用
prepareMainLooper()
方法,进行Looper的创建,无需我们手动进行创建/** * Initialize the current thread as a looper, marking it as an * application's main looper. See also: {@link #prepare()} * * @deprecated The main looper for your application is created by the Android environment, * so you should never need to call this function yourself. */ @Deprecated public static void prepareMainLooper() { prepare(false); //本质上也是调用了prepare()方法进行Looper的创建 synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }
-
当前线程是子线程,那么我们就需要调用
prepare()
方法进行Looper的主动创建public static void prepare() { prepare(true); }
其实这两种情况最终都是通过
prepare(boolean quitAllowed)
去创建的Looper,只是传入的参数不同,那么这个参数代表的是什么意义呢?private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { //在调用Looper构造方法之前,会先从ThreadLocal中获取Looper,如果不为null,说明Looper已经被创建过了,会直接抛异常,确保每一个线程只有一个Looper throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
查看上面的Looper的构造方法,其实可以看到,参数quitAllowed是传入MessageQueue的构造方法中的,从参数命名其实也可以看出来,这个代表是否可以退出:
- 主线程传入的是false,代表主线程的MessageQueue是不允许退出的
- 子线程传入的是true,代表子线程的MessageQueue是允许退出的
-
-
总结一下关于Looper的创建:
- 每一个线程只有唯一存在的Looper,如何保证的呢?
- 通过ThreadLocal对Looper进行存储
- Looper构造函数私有化,保证外部无法直接调用Looper构造方法进行构建Looper
- 在每次调用
prepare()
进行Looper构建之前,都会先从ThreadLocal中获取Looper,只有在获取出来的返回值为空时,才去创建Looper
- 每一个线程只有唯一存在的Looper,如何保证的呢?
Handler中的MessageQueue
-
在上面Handler创建的分析中,可以看到,Handler中的MessageQueue其实就是Looper中维护的MessageQueue,Handler中的Queue是在Looper中创建的,而又因为同一个线程只会存在唯一的一个Looper,那么相对的,同一个线程也就只有一个MessageQueue
-
从MessageQueue中存放和获取消息在上面的Handler流程中都已经完成了分析,这里主要看一下MessageQueue的退出
void quit(boolean safe) { if (!mQuitAllowed) { //是否允许退出,主线程的Queue该标志位为false,不允许退出,直接抛异常;子线程为true throw new IllegalStateException("Main thread not allowed to quit."); } synchronized (this) { if (mQuitting) { //判断Queue是否已经退出 return; } mQuitting = true; //置位退出标志位 if (safe) { removeAllFutureMessagesLocked(); } else { removeAllMessagesLocked(); } // We can assume mPtr != 0 because mQuitting was previously false. nativeWake(mPtr); //唤醒MessageQueue.next(),使MessageQueue返回为null,从而在Looper的loop循环处也直接退出 } } @UnsupportedAppUsage Message next() { ... for (;;) { ... //此时mQuitting标志位为true,直接返回null if (mQuitting) { dispose(); return null; } ... } ... } public static void loop() { ... for (;;) { ... //此时获取的msg==null Message msg = queue.next(); if (msg == null) { //直接退出了looper循环 return; } ... } ... }
Message的创建
-
接下来我们来分析一下Message的创建,Message的创建主要有两种方式;
new Message()
obtain()
这两种方式都可以用来创建Message,但是更加推荐使用obtain方法来进行Message的创建,我们可以看一下
obtain()
方法的实现:public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { //当sPool不为空,会直接拿sPool,否则再创建新的Message Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); }
可以看到,在创建新的Message之前,会先服用之前的sPool,那么这个sPool又是哪里来的呢?在上面的Handler调用链的分析中,在
loop()
方法的最后:msg.recycleUnchecked()
,看一下recycleUnchecked()
在干什么@UnsupportedAppUsage void recycleUnchecked() { // Mark the message as in use while it remains in the recycled object pool. // Clear out all other details. flags = FLAG_IN_USE; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; sendingUid = UID_NONE; workSourceUid = UID_NONE; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } }
可以看到,在Message执行结束,会调用
recycleUnchecked()
方法将消息进行回收保存,而在obtain()
方法中,用的就是这里保存的消息。 -
使用
obtain()
方法其实就是采用了享元设计模式,目的在于内存的复用。
同步屏障
-
在分析Handler调用链的时候,在
MessageQueue.next()
方法中,是存在同步屏障的场景的,那么何为同步屏障呢?如何设置同步屏障呢? -
在之前的分析中,
next()
方法取消息的时候,会判断msg.target == null
,而target就是Message所持有的Handler;在我们正常创建消息的时候,调用Handler.enqueueMessage()
方法,都存在对target的赋值:msg.target = this
,也就是说正常创建消息,target都是不为空的,那么怎么样的操作才能使得target==null
呢?怎么样的消息才是异步消息呢?
异步消息的创建
-
异步消息的创建有两种方式:
-
直接设置消息为异步消息
Message msg = mMyHandler.obtainMessage(); msg.setAsynchronous(true); mMyHandler.sendMessage(msg);
-
构建一个发送异步消息的Handler
Handler mMyHandler = new createAsync(mLooper); Message msg = mMyHandler.obtain(); mMyHandler.sendMessage(msg);
如上两种方式都可以创建一个异步消息,其实本质上都是在构建消息的时候,将async设为true
-
-
如此只是创建了异步消息,实际并不会直接被优先执行,因为不存在同步屏障,所以他们并不会被直接执行。那么什么是同步屏障呢?
同步屏障的概念
-
同步屏障就是阻碍同步消息的传递,只让异步消息进行传递。
-
概念如上,那么在代码中怎么样的形式是同步屏障呢?其实查看
MessageQueue.next()
方法中获取消息的方式可以知晓:Message next() { ....//省略无关代码 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()); //查找队列中的异步消息 ....//省略无关代码 }
当消息队列中存在
msg.target==null
这样的消息时,就是开启了同步屏障,那么这个消息是怎么来的呢?
同步屏障的开启
-
同步屏障的开启,即往消息队列中塞入
target==null
的消息,那么怎样塞入这样的消息呢?其实就是通过调用MessageQueue.postSyncBarrier()
方法实现的:@UnsupportedAppUsage @TestApi 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++; //从消息池中取出消息后,并没有对msg.target进行赋值,即target==null 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.next msg.next = p; prev.next = msg; } else { msg.next = p; mMessages = msg; } return token; } }
可见,在
postSyncBarrier()
方法中,并没有对msg.target
进行赋值,即使得msg.target == null
-
那么有了同步屏障之后,异步消息是如何被处理的呢?
异步消息的处理
-
在上面针对
MessageQueue.next()
消息的获取方法中,其实就是在获取消息时发现存在同步屏障,即下一个将要传递的消息的target为空时,就不再处理同步消息,而是去查找队列中第一个async为TRUE的异步消息,然后将该消息传递出去,而且可以看到,如果不主动移除同步屏障,即target==null
的消息,每次获取都会只去查找队列中的异步消息,不会获取同步消息,执行流程如下图所示: -
那么如何移除同步屏障呢?
同步屏障的移除
-
同步屏障的移除需要调用
MessageQueue.removeSyncBarrier()
方法@UnsupportedAppUsage @TestApi 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; //遍历整个Queue,找到那个message.target==null的消息 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; //当prev不为null,即同步屏障消息不是队头 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); } } }
总结
- 同步屏障的设置可以方便地处理那些优先级较高的异步消息。当我们调用
MessageQueue.postSyncBarrier()
并设置消息的setAsynchronous(true)
时,target 即为 null ,也就开启了同步屏障。当在消息轮询器 Looper 在loop()
中循环处理消息时,如若开启了同步屏障,会优先处理其中的异步消息,而阻碍同步消息。
优秀文章链接
- [揭秘 Android 消息机制之同步屏障:target==null ?](揭秘 Android 消息机制之同步屏障:target==null ? (juejin.cn))