Handler原理浅析

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()方法,而纵观所有的发送函数,sendpost最大的区别就是:

    • 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);
    }
    

    最终是调用到了MessageQueueenqueueMessage()方法

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使用时消息传递和分发的整个流程,在消息传递的过程中,我们发现涉及了LooperMessageQueue这两个类,那么这两个类是什么,又是在何处被初始化的呢?

  • 首先我们来看一下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中的Queue

    public 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呢?分两种情况:

    1. 当前线程是主线程,那么系统会主动调用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();
          }
      }
      
    2. 当前线程是子线程,那么我们就需要调用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的构造方法中的,从参数命名其实也可以看出来,这个代表是否可以退出:

    1. 主线程传入的是false,代表主线程的MessageQueue是不允许退出的
    2. 子线程传入的是true,代表子线程的MessageQueue是允许退出的
  • 总结一下关于Looper的创建:

    • 每一个线程只有唯一存在的Looper,如何保证的呢?
      1. 通过ThreadLocalLooper进行存储
      2. Looper构造函数私有化,保证外部无法直接调用Looper构造方法进行构建Looper
      3. 在每次调用prepare()进行Looper构建之前,都会先从ThreadLocal中获取Looper,只有在获取出来的返回值为空时,才去创建Looper

Handler中的MessageQueue

  • 在上面Handler创建的分析中,可以看到,Handler中的MessageQueue其实就是Looper中维护的MessageQueueHandler中的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的创建主要有两种方式;

    1. new Message()
    2. 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呢?怎么样的消息才是异步消息呢?

异步消息的创建

  • 异步消息的创建有两种方式:

    1. 直接设置消息为异步消息

      Message msg = mMyHandler.obtainMessage();
      msg.setAsynchronous(true);
      mMyHandler.sendMessage(msg);
      
    2. 构建一个发送异步消息的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为空时,就不再处理同步消息,而是去查找队列中第一个asyncTRUE的异步消息,然后将该消息传递出去,而且可以看到,如果不主动移除同步屏障,即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 ,也就开启了同步屏障。当在消息轮询器 Looperloop()中循环处理消息时,如若开启了同步屏障,会优先处理其中的异步消息,而阻碍同步消息。

优秀文章链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值