Handler 二期 —细节补充

之前说过一次Handler,主要介绍了sendMessage 到 handleMessage的流程,具体的细节并没有过多涉及,本文会补充Handler的两个细节:

  • 阻塞唤醒机制
  • 内存屏障

Looper

Looper的创建:

Looper构造方法:
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

Looper只有一个私有构造方法,在构造方法中,会创建一个MessageQueue对象;

主线程Looper对象的创建:
    public static void prepareMainLooper() {
        //  分析1
        prepare(false);
        synchronized (Looper.class) {
                 //  分析2
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            //  分析3
            sMainLooper = myLooper();
        }
    }
    
    //  分析1
    private static void prepare(boolean quitAllowed) {
        //  分析2
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
    
    //  分析3
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

在ActivityThread的main中调用prepareMainLooper()为当前App的主线程构造一个Looper对象;

  1. 分析1: 调用prepare() new一个Looper

  2. 分析: 保证一个线程只能创建一个Looper对象

  3. 分析3:通过ThreadLocal做到Looper的线程隔离

子线程Looper的创建就是调用prepare(),注意这里的quitAllowed参数,表示Looper是否可以quit(),子线程可以,主线程不可以;

Loop的循环:loop()

    public static void loop() {

        //  获取当前Looper的MessageQueue对象
        final MessageQueue queue = me.mQueue;

       //  死循环,不断的从MessageQueue中取消息
        for (;;) {
            //  分析1
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            try {
                //  分析2
                msg.target.dispatchMessage(msg);
            } 
        }
    }

在ActivityThread的main中构造了主线的Looper对象后,会调用Looper的loop(),在loop()首先获取当前Looper绑定的MessageQueue对象,然后在一个死循环里不断的取Message,分发给对应的Handler处理

  1. 分析1: 调用MessageQueue的next()获取一个Message对象,这个方法是一个阻塞方法,后面会详细讲;
  2. 分析2:分发给对应的handler处理;上篇文章有详细介绍;

Looper循环的退出:quit()

    public void quit() {
        mQueue.quit(false);
    }

Looper的退出调用了MessageQueue的退出,具体内容在MessageQueue部分展开;

MessageQueue

在具体介绍MessageQueue之前,我们先看一下MessageQueue是如何保存Message的:

public final class MessageQueue {
    Message mMessages;
  }

public final class Message implements Parcelable {
    /*package*/ Message next;
  }

我们可以看到MessageQueue的内部只有一个Message成员,并没有其他的数据结构存放Message; 而Message本身是一个链表的结点;

MessageQueue获取Message :next()

    Message next() {
        //  分析1
        int nextPollTimeoutMillis = 0;
        for (;;) {
              //  分析2
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {

                if (msg != null) {
                    // 分析3
                    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 {
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        return msg;
                    }
                } else {
                    // No more messages.   // 分析4
                    nextPollTimeoutMillis = -1;
                }
                //  分析5
                // Process the quit message now that all pending messages have been handled.  
                if (mQuitting) {
                    dispose();
                    return null;
                }

    }
  • 分析1:nextPollTimeoutMillis字段,用于判断是否阻塞
  • 分析2:nativePollOnce()这是一个native方法,用于实现next()的阻塞,根据nextPollTimeoutMillis的大小,-1表示一直阻塞等到有人唤醒,0表示不阻塞,大于0表示阻塞多少时间后自动唤醒;
  • 分析3:MessageQueue是一个优先级队列,当取出的Message还没有没有到执行时间时,会计算出一个时间差,通过nativePollOnce()取阻塞一定时间;
  • 分析4:如果没有取到Message,将nextPollTimeoutMillis字段置为-1,一直阻塞等待唤醒;
  • 分析5:检查mQuitting字段,上文的Looper退出循环调用quit会将此字段设置为true,回收当前的MessageQueue;

MessageQueue添加Message:enqueueMessage()

    boolean enqueueMessage(Message msg, long when) {

        synchronized (this) {

            //  分析1
            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;
            //  分析:2
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
            //  分析3
            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

MessageQueue是一个优先级队列,在使用Handler提交Message的时候,会附加一个时间,这个时间会通过when字段保存,在添加到队列的时候,会通过插入排序的方式找到当前Message在队列的合适位置;

  • 分析1: Message的优先级when字段
  • 分析2:插入排序
  • 分析3:唤醒操作,当next()中的nativePollOnce()传入-1时,会一直阻塞,在这里会唤醒这个阻塞;

Message

Message的复用:obtain()

    private static Message sPool;  //   缓存链表

    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

在Message类的内部会有一个Pool链表,用来复用Message对象,调用obtain()创建Message时会优先从Pool中取,如果娶不到才会去new;

Message Pool的添加:
    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 = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

    public void recycle() {
        if (isInUse()) {
            if (gCheckRecycle) {
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        recycleUnchecked();
    }

recycleUnchecked()中,先将Message对象的所有成员清空,如果Pool没有超过容量的话就添加到pool中;外部使用recycle()复用Message,这个方法的调用时机很多,ActivityThread的H,Wifi服务都用了这个回收

内存屏障:

在安卓中,整个App的运行都是在Loop.loop()死循环中完成了,包括我们的项目逻辑,安卓的系统逻辑,比如屏幕的刷新等,安卓系统规定了屏幕一般16ms刷新一次,刷新操作是由ViewRootImpl来实现的,触发屏幕刷新的机制也是一个Message消息,但是我们刷新屏幕的Message必须是立刻执行的,由于MessageQueue是优先级队列的原理,无法做到实时的刷新,就提出了Handler内存屏障的概念;

Message节点被分成两种:同步和异步
message.setAsynchronous(true);   //  设置为异步Message

Message有一个字段,用于表示异步还是同步,默认是同步,可以通过setAsynchronous()设置,在一些特殊的场景下,比如View的刷新,必须立刻执行,就需要设置异步Message;

内存屏障的打开与关闭:

单纯的设置同步和异步是无法保证立刻执行的,还必须打开内存屏障:内存屏障的本质是一个target==null的Message,当loop的时候发现有内存屏障的时候,此时只会处理异步Message,保证了异步Message的立刻执行;

MessageQueue.postSyncBarrier()
   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.next
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

通过postSyncBarrier方法,就可以添加一个target==null的Message,表示打开了内存屏障;

MessageQueue.removeSyncBarrier()
    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);
            }
        }
    }

通过removeSyncBarrier删除内存屏障节点,关闭内存屏障;

ViewRootImpl.scheduleTraversals()
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

    void unscheduleTraversals() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
            mChoreographer.removeCallbacks(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        }
    }

在ViewRootImpl内中,就是使用这两个方法做到的立刻刷新

内存屏障的目的和使用介绍了,我们看看MessageQueue是如何实现异步消息的立刻执行的:

MessageQueue.next()
    Message next() {
...
        for (;;) {
            synchronized (this) {
                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());
                }
...
        }
    }

其他多余的代码就不贴了,上面已经贴过了,当发现一个target== null的Message的时候,会用一个do while 去取异步的Message;

你可能感兴趣的:(Handler 二期 —细节补充)