美文网首页
Hanlder源码分析(包括同步屏障,异步消息)

Hanlder源码分析(包括同步屏障,异步消息)

作者: koller | 来源:发表于2019-01-04 17:22 被阅读0次

    1 创建Handler

    众所周知,在子线程直接创建Handler一定会报错,如图


    1.png

    意思也很明确,必须要调用Looper.prepare(),才能创建Handler,因为整个Handler的消息循环机制是建立在Looper之上.

    那为什么主线程不用调用Looper.prepare就可以创建Handler呢?
    因为在ActivityThread的main函数里面,帮我们调用了Looper.prepare()

    public static void main(String[] args) {
            //省略部分代码
            ...
            Looper.prepareMainLooper();
    
            ActivityThread thread = new ActivityThread();
            thread.attach(false);
    
            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }
    
            if (false) {
                Looper.myLooper().setMessageLogging(new
                        LogPrinter(Log.DEBUG, "ActivityThread"));
            }
    
            // End of event ActivityThreadMain.
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            Looper.loop();
    
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }
    

    而使用两次Looper.prepare()则会报一下错误


    2.png

    因为每个Thread仅仅只能获取一个Looper.

    搞清楚了Looper.prepare,我们来看一下为什么必须要调用它才能创建Handler.

    2 Looper.prepare

    3.png
    4.png
    5.png
    6.png

    而Looper的mQueue则是在构造函数中创建的,如图所示,即我们调用prepare的时候
    到此,prepare的代码全部执行完了。

    从上面的图6,可以看到prepare主要里是这句代码
    sThreadLocal.set(new Looper(quitAllowed));
    1 新建了一个Loopper对象
    2 set到sThreadLocal里面
    3 sThreadLocal又新建了一个Map来保存当前的key(即当前的Thread)与value(即新建的Looper)
    4 Looper创建了MessageQueue

    再来看new Handler做了什么事情

    3 Handler构造函数

    7.png

    可以看到这句 mLooper = Looper.myLooper();关键代码,将Looper.myLooper赋值给Handler自己的mLooper,不然就报那个知名的错误。

    mQueue = mLooper.mQueue;//将looper的mQueue赋值给Handler的mQueue

    8.png
    image.png

    可以看到Looper.myLooper()是将prepare的里面创建的Looper再取出来。
    可以得知 每次新建Handler的时候,得先将Looper创建好。

    4 Looper.loop

    一般我们创建好Handler之后,都需要在调用Looper.loop,我们看看它具体做了什么事情.

    public static void loop() {
            final Looper me = myLooper();
            //省略部分无效代码
            final MessageQueue queue = me.mQueue;
    
            for (;;) {
                Message msg = queue.next(); // might block
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
              try {
                    msg.target.dispatchMessage(msg);
                    end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
                //省略部分无效代码
                msg.recycleUnchecked();
            }
        }
    

    至此,我们可以看到,loop是获取了,MessageQueue,然后启动死循环,不断调用queue.next直到消息队列里,没有任何消息了,才跳出循环.
    至此,创建Handler的部分结束了,我们来看看Handler如何SendMessage的

    5 Handler.SendMessage

    当我们调用SendMessage时候,最终会调用的方法是这个


    9.png
    10.png
    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) {
    
                msg.markInUse();
                msg.when = when;
                Message p = mMessages;
                boolean needWake;
                if (p == null || when == 0 || when < p.when) {// 如果是第一个进入到消息队列的消息且delay的时间最小(最小为0),则把其设置为消息队列的头
                    // New head, wake up the event queue if blocked.
                    msg.next = p;
                    mMessages = msg;
                    needWake = mBlocked;
                } else {
                //链表操作,增加一个消息实体
                    Message prev;
                    for (;;) {
                        prev = p;
                        p = p.next;
                        if (p == null || when < p.when) {//根据delay的时间决定插在队列的什么位置
                            break;
                        }
                    }
                    msg.next = p; // invariant: p == prev.next
                    prev.next = msg;
                }
            }
            return true;
        }
    

    可以看到,MessageQueue.enqueueMessage会将所有的被sendMessage发送的Message增加到链表里。
    由于Looper.loop的里面调用queue.next()的关系,(一直查询是否有新消息到来),如果查询到新消息,则进行msg.target.dispatchMessage(msg)。而msg的target则是在enqueueMessage做msg.target = this了,既当前发送此消息的Handler.
    此时又回到了Handler的dispatchMessage

    6 Handler.dispatchMessage

    11.png

    可以看到,dispatchMessage并没有做什么复杂的操作,仅仅就是判断了Message自身是否有CallBack,Handler是否设置了CallBack,如果都没有,则调用Handler的HandleMessage方法。

    同步屏障

    ViewRootImpl.java

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

    Choreographer.postCallback实际上调用了postCallbackDelayedInternal,可以看到setAsynchronous(true);即设置为异步消息。

    private void postCallbackDelayedInternal(int callbackType,
                Object action, Object token, long delayMillis) {
            if (DEBUG_FRAMES) {
                Log.d(TAG, "PostCallback: type=" + callbackType
                        + ", action=" + action + ", token=" + token
                        + ", delayMillis=" + delayMillis);
            }
    
            synchronized (mLock) {
                final long now = SystemClock.uptimeMillis();
                final long dueTime = now + delayMillis;
                mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
    
                if (dueTime <= now) {
                    scheduleFrameLocked(now);
                } else {
                    Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                    msg.arg1 = callbackType;
                    msg.setAsynchronous(true);//设置为异步消息
                    mHandler.sendMessageAtTime(msg, dueTime);
                }
            }
        }
    

    总结:
    1 调用Looper.prepare是为了创建Looper,然后创建Map,根据CurrentThread保存Looper。Looper创建了MessageQueue
    2 new Handler的时候会将Looper与Hanlder绑定,并且将Looper的MessageQueue与Handler的mQueue绑定
    3 Looper.loop是为了启动消息循环不断查询(queue.next())是否有新消息到来,可能会阻塞
    4 Handler.SendMessage则是将Message.target与Handler绑定,并且将Message插入到MessageQueue中去(根据delay插入,越小越前)
    5 Looper.loop中查询到有新消息来了后,将会调用Message.target.dispatchMessage,将msg分发出去
    6 Hanlder.dispatchMessage将进行msg的callback,Hanlder的callback判断,最后才调用HandleMessage.
    即Message的CallBack优先级最高,Hanlder的CallBack其次,HandleMessage最低.
    7 sThreadLocal是确保每个Thread里有且仅有一个Looper的关键,因为会去获取线程独有的ThreadLocal.ThreadLocalMap作为Map,然后把自己作为key,来保存looper,每次prepare则先会访问线程独有的ThreadLocal.ThreadLocalMap来判断是否有value
    8 Message也分同步消息与异步消息,有两种方式发送异步消息, Message.setAsynchronous(True)与Hanlder.createAsync(),如果不设置则都为同步消息。在系统的ViewRootImpl里面Chreograher就是发送异步消息来绘制UI 即perfromTraversal会提前于所有同步消息执行
    9 延时消息 是利用Message的when来对比,插入到哪里(小的前,大的后)

    相关文章

      网友评论

          本文标题:Hanlder源码分析(包括同步屏障,异步消息)

          本文链接:https://www.haomeiwen.com/subject/tckkrqtx.html