Handler 源码分析

作者: PerryWong | 来源:发表于2018-08-03 16:34 被阅读119次

         为了深入了解Handler,我们对Handler进行源码分析,看看它究竟是如何完成线程间通信的。

    从Handler构造出发

         既然是源码分析,为什么突然回到Handler的构造了呢?在我的Handler使用机制及四个组成部分文章中,当我们在非主线程创建handler对象时会出现一个异常。如下:

    05-30 15:26:07.490 711-773/com.perry.handler E/AndroidRuntime: FATAL EXCEPTION: Thread-18868
                                                                   Process: com.perry.handler, PID: 711
                                                                   java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
                                                                       at android.os.Handler.<init>(Handler.java:209)
                                                                       at android.os.Handler.<init>(Handler.java:123)
                                                                       at com.perry.handler.OnBackgroundActivity$2.run(OnBackgroundActivity.java:71)
                                                                       at java.lang.Thread.run(Thread.java:818)
    

         出现这个问题实际上是因为,handler需要Looper对象,来进行内部的全局变量的初始化。


    Handler 构造函数.png
    public Handler(Callback callback, boolean async)
    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    

         通过查看源码发现,handler共7个构造函数,使用时只能使用4个,最后都会调用执行如上2个构造方法,而这2个构造方法大同小异,所以我们挑个简短的来看看。发现handler需要looper对象来初始化全局变量Looper及MessageQueue,而MessageQueue是从looper中取出的。MessageQueue用于进程间传递消息,Looper则在该线程内进行消息的派发。

    handler.sendMessage

         我们通过构造找到了handler的2个协同者,Looper与MessageQueue。那我们发个消息,看看他们都做了些什么!?
         无论调用post方法还是sendMessage方法,handler都会从上到下依次调用如下方法。

    sendMessageDelayed(Message msg, long delayMillis)
    sendMessageAtTime(Message msg, long uptimeMillis)
    enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
    

    handler.enqueueMessage

         此时,到了enqueueMessage方法,就证明这条消息确实发送了,该做下一步处理。

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    

         该方法中对message设置了target,将当前的handler存于message中,用于之后message找到自己该去的handler,而后将massage交给MessageQueue。调用queue.enqueueMessage(msg, uptimeMillis)方法,将消息放入到消息队列中。强调一下,这里的queue就是我们前面说到的在构造方法中从looper中取到的消息队列。

    Looper.loop()

         到了这里,我们发现handler将消息通过MessageQueue放入到消息队列中,那谁把消息取出来进行派发的呢?草丛三兄弟,跳出来两个了,还剩Looper一人默默的蹲在那,那我们来看看他究竟干了啥?!

    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;   
                }
                msg.target.dispatchMessage(msg); // 发送消息, 其中target就是我们之前存入的Handler
            }
        }
    

         精简了一下该方法,可以看到,Looper通过loop方法,不停的从MessageQueue中取出消息,最终调用了handler的dispatchMessage(msg)方法将消息派发给handler自己。

    handler.dispatchMessage

         最后,消息派发给handler,会先判断msg的callback对象(实际上是runnable对象)是否为空,如果不为空则执行handleCallback(msg)方法,callback.run();而如果为空就会调用handleMessage方法,完成回调。(这也是为什么我们构造Handler时,实现了抽象方法handleMessage而post(runnable)时,为什么不调用handleMessage方法的原因)。

    // msg.callback实际上是Runnable对象
    // mCallback是一个实现了handleMessage(msg)的回调接口
    public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg); // msg.callback.run();
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    

         那么来画个图总结一下。

    Handler 源码分析图示.png
         图中我们从主线程中创建Handler,在子线程中发送消息,而这一系列的配合操作,需要仔细看图,不懂的可以回看文章。
         上一篇:Handler使用机制及四个组成部分

    相关文章

      网友评论

      本文标题:Handler 源码分析

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