Android Handler 源码分析3

这是我参与8月更文挑战的第18天,活动详情查看:8月更文挑战

以上是进入消息队列的分析,handler调用sendMessage方法的最终将message对象传进messagequeue。

完毕,那么消息是怎么从消息队列出来的呢?
这时我们要回看ActiviryThread的main方法,去寻找点线索。源码在上面已贴出。
发现了倒数第二行的Looper.loop(),简单理解就是消息循环执行循环操作。

这里一定能满足我们的好奇心。那么跟进。loop方法的源码如下:

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

复制代码

抓重点看就好。首先是调用myLooper方法获取到Looper对象,这里是没问题的,那就继续

MessageQueue queue = me.mQueue
复制代码

然后从Looper对象中取出关联的消息队列,
接着进入了一个死循环,调用messagequeue的next方法取出message对象。这个next方法我没看懂,所以不贴源码出来分析了,反正next方法的作用就是取出message对象的。有兴趣的同学自己去研究研究吧。
到这里可以总结一下:通过Looper.prepare()来创建Looper(消息循环)对象,然后通过Looper.loop()来执行消息循环,Looper.prepare()和Looper.loop()通常是成对出现的。
好,回来继续
经过一系列的判断后会来到这里,很重点

msg.target.dispatchMessage(msg);
复制代码

上面已经分析,msg.target就是handler,那么这行代码的意义就是调用handler的dispatchMessgage的方法去分发消息,
那么看到dispatchMessage的方法源码,相信谜底就要揭开了

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}
复制代码

从上述的代码跟踪中,都没有发现给message的callback字段赋值,那么我们就先不搭理,默认callback为空,那么就一定会来到handleMessage方法。

message对象传递到了handleMessage方法。

/**
 * Subclasses must implement this to receive messages.
 */
public void handleMessage(Message msg) {
}
复制代码

handleMessage是一个空方法,需要我们去重写。

至此,海阔天空。完美的从子线程切换到主线程,我不得不说Android的源码设计是多么精彩。
以上就是handler使用sendMessage方法发送消息的源码分析。

为什么我会这么说呢?因为handler还有一种方法可以发送消息,是post方法,理解这个方法。可以解决刚才没搭理的那个message的callback字段的问题。看到post方法源码

public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}
复制代码

接收一个实现了Runable接口的对象,然后将其传进getPostMessage()方法。跟进getPostMessage()方法看看

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}
复制代码

其实就是将Runable包装成message的callback嘛。
所以,如果我们使用post方法发送消息,callback字段是不为空的,那么就会执行handleCallback()方法,而不是执行handleMessage方法了。

handleCallback方法源码如下:

private static void handleCallback(Message message) {
    message.callback.run();
}
复制代码

直接是调用run方法,表明我们直接在run方法里进行UI操作就行了。

我们发现不管是使用post方法还是sendMessage方法来发送消息,最终都会调用sendMessageDelayed方法。handler将消息追加到消息队列中的过程都是一样的,然后Looper不断的从MessageQueue中取出消息,并由handler去分发消息,处理消息,这样就构成了完善的Android消息机制体系。

最后在总结一下,主线程创建 Handler,关联 Looper 和 MessageQueue。在子线程中执行业务逻辑获取到数据后,此时我们需要切换到主线程,那就创建一个Message对象,包装一些数据,然后调用 Handler 的 sendMessage 方法发送消息到消息队列 MessageQueue。神奇的是,发送的时候,Message的 target 字段赋值上了 Handler,也就是说 Handler 对象跟着 Messgae 对象进入了消息队列 MessageQueue。在程序启动的时候, Looper 就调用了 loop 方法,不断的从消息队列 MessageQueue 中取出消息,一旦 Messgae 进入消息队列,Looper 从消息队列取出消息后,Messgae 中包装的 Handler,会调用 dispatchMessage 去分发消息,将消息传进 handleMessage 方法,这时就切换到了主线程。这就是消息机制。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享