美文网首页
看源码分析Handler的执行流程

看源码分析Handler的执行流程

作者: 依然淳熙 | 来源:发表于2019-04-15 14:12 被阅读0次

    记得之前面试时,面试官就问了Handler的工作流程,当时脑子里有MessageQueue、Looper等类名但具体怎么执行的却说不明白。于是乎面试也就失败了。想想还是挺菜鸡的。。以一个简单的例子通过源码来走一遍Handler的执行流程。

    Handler handler=new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                return false;
            }
        });
    
    
      protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_test);
            Message message=Message.obtain();
            message.arg1=1;
            handler.sendMessage(message);
     }
    

    1.MessageQueue

    就是这么一个最简单的例子先进入sendMessage方法,然后又跳到了sendMessageDelayed方法,最后跳到了sendMessageAtTime方法,在sendMessageAtTime方法中看到了熟悉的面孔MessageQueue,初始化了一下MessageQueue,之后进入了enqueueMessage方法。

    public final boolean sendMessage(Message msg) {
            return sendMessageDelayed(msg, 0);
    }
    
    
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
            if (delayMillis < 0) {
                delayMillis = 0;
            }
            return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    
    
    public boolean sendMessageAtTime(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);
    }
    

    enqueueMessage方法:

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    
    
    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.next
                    prev.next = msg;
                }
    
                // We can assume mPtr != 0 because mQuitting is false.
                if (needWake) {
                    nativeWake(mPtr);
                }
            }
            return true;
        }
    

    在enqueueMessage方法中将msg.target设置为当前的Handler,也就是绑定了Handler,然后进入enqueueMessage中,通过模拟代码执行,有了一个重要的结论,我们每发送一个消息都被保存到了 MessageQueue 消息队列中,消息队列中采用的是单链表的方式。

    // 发送 Message1
    Message message1 = new Message();
    mHandler.sendMessageDelayed(message1, 500);
    
    // 发送 Message2
    Message message2 = new Message();
    mHandler.sendMessage(message2);
    
    // 发送 Message3
    Message message3 = new Message();
    mHandler.sendMessageDelayed(message3, 1000);
    

    主要看这段来模拟:

                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.next
                    prev.next = msg;
                }
    

    模拟如下:

    message1.when=500;
    Message p = mMessages;
    而mMessages=null; 所以Message p =null;
    因为p =null,所以进入if
    message1.next = null;
    mMessages = message1;
    
    所以发送完message1之后链表应该是这样的: 发送完message1

    然后发送message2:

    message2.when=0;
    Message p = mMessages; //在发送message1是已经将mMessages赋值为 message1
    所以
    p=message1;
     if (p == null || when == 0 || when < p.when) 
    因为when=0,所以进入if
    message2.next=p;即
    message2.next=message1
    mMessages =message2;
    
    所以发送完message2之后链表应该是这样的: 发送完message2

    然后发送message2:

    message3.when=1000;
    Message p =message2;
     if (p == null || when == 0 || when < p.when)  不满足if条件,进入else
      Message prev;
    进入for(;;)循环
    prev = message2;
    p = p.next;即
    p=message1;
     if (p == null || when < p.when) //  不满足if继续循环
     prev = p;
     即
    prev = message1;
    p = p.next;即   
    p=null;
    if (p == null || when < p.when) // 满足条件进入if 跳出
    msg.next = p;即
    message3.next=null;
    prev.next = msg; 即
    message1.next=message3                  
             
    

    所以发送完message3之后应该是这样的:


    发送完message3

    得出结论消息在队列中是按when和先后顺序排列的。

    2. Loop 消息循环

    我们始终没有看到 Handler 调用 handleMessage() 方法,到底什么时候会执行这个方法?
    书上说在子线程中使用handler要先

     Looper.prepare();
     Handler handler = new Handler();
     Looper.loop();
    

    否则会报错,而在MainActivity即主线程中直接使用不会报错。原因就是在启动Activity过程中在ActivityThread帮我们已经调用过了代码如下:

     ActivityThread.java main()
     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");
        }
    
    
    public static void prepareMainLooper() {
            prepare(false);
            synchronized (Looper.class) {
                if (sMainLooper != null) {
                    throw new IllegalStateException("The main Looper has already been prepared.");
                }
                sMainLooper = myLooper();
            }
        }
    
    
    private static void prepare(boolean quitAllowed) {
            if (sThreadLocal.get() != null) {
                throw new RuntimeException("Only one Looper may be created per thread");
            }
            sThreadLocal.set(new Looper(quitAllowed));
        }
    
    public void set(T value) {
            Thread currentThread = Thread.currentThread();
            Values values = values(currentThread);
            if (values == null) {
                values = initializeValues(currentThread);
            }
            values.put(this, value);
        }
    
    public static void loop() {
            final Looper me = myLooper();
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
            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();
    
            for (;;) {
                Message msg = queue.next(); // might block
                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
                Printer logging = me.mLogging;
                if (logging != null) {
                    logging.println(">>>>> Dispatching to " + msg.target + " " +
                            msg.callback + ": " + msg.what);
                }
    
                msg.target.dispatchMessage(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);
                }
    
                msg.recycleUnchecked();
            }
        }
    
    public static @Nullable Looper myLooper() {
            return sThreadLocal.get();
     }
    

    在prepare(false)方法中创建一个Looper对象并把它存储到sThreadLocal中并且与当前线程绑定。
    当调用 loop()方法再在从sThreadLocal中取出Looper对象。ThreadLocal用来保证一个线程只有一个 Looper 对象,这样就保证了线程的安全。接下来是一个for(;;)死循环调用队列中msg.target.dispatchMessage。这样就找到了在handleMessage中可以收到信息的原因。message的target是在加入队列时设置的。

     /**
         * Handle system messages here.
         */
        public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    

    总结:Looper.prepareMainLooper() 创建了一个 Looper 对象,而且保证一个线程只有一个 Looper;Looper.loop() 里面是一个死循环,不断的从 消息队列 MessageQueue 中取消息,然后通过 Handler 执行。

    相关文章

      网友评论

          本文标题:看源码分析Handler的执行流程

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