写点什么

Android 进阶:用最详细的方式解析 Android 消息机制的源码

用户头像
Android架构
关注
发布于: 2021 年 11 月 05 日

当我们在 UI 线程中创建 Handler 的时候 sThreadLocal 里的 Looper 从哪里来的?


我们知道,我们获取主线程的 Looper 需要调用 getMainLooper()方法,代码如下:


public static Looper getMainLooper() {


synchronized (Looper.class) {


return sMainLooper;


}


}


所以我们跟踪一下这个变量的赋值,发现在方法 prepareMainLooper()中有赋值,我们去看看代码:


public static void prepareMainLooper() {


prepare(false);


synchronized (Looper.class) {


if (sMainLooper != null) {


throw new IllegalStateException("The main Looper has already been prepared.");


}


sMainLooper = myLooper();


}


}


第一步调用了 prepare(false),这个方法我们刚才已经看了,是创建一个 Looper 对象,然后存到 sThreadLocal 中;


然后判断 sMainLooper 是否为空,空则抛出异常


sMainLooper 不为空,则 sMainLooper = myLooper()


至此 sMainLooper 对象赋值成功,所以,我们需要知道 prepareMainLooper()这个方法在哪调用的,跟一下代码,就发现在 ActivityThread 的 main 方法中调用了 Looper.prepareMainLooper();。现在真相大白:


当我们在 UI 线程中创建 Handler 的时候 sThreadLocal 里的 Looper 是在 ActivityThread 的 main 函数中调用了 prepareMainLooper()方法时初始化的


ActivityThread 是一个在一个应用进程中负责管理 Android 主线程的执行,包括活动,广播,和其他操作的类

[](

)3、初始化一个 MessageQueue mQueue


从代码里我们看出这里直接调用了:mLooper.mQueue 来获取这个对象,那这个对象可能在 Looper 初始化的时候就产生了。我们去看看 Looper 的初始化代码:


private Looper(boolean quitAllowed) {


mQueue = new MessageQueue(quitAllowed);


mThread = Thread.currentThread();


}


代码很简单,就是创建了 MessageQueue 的对象,并获得了当前的线程。


至此,Handler 的创建已经完成了,本质上就是获得一个 Looper 对象和一个 MessageQueue 对象!


###二、使用 Handler 发送消息


Handler 的发送消息的方式有很多,我们跟踪一个方法 sendMessage 方法一直下去,发现最后竟然调用了 enqueueMessage(queue, msg, uptimeMillis),那我们看看这个方法的代码:


private boolean


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {


msg.target = this;


if (mAsynchronous) {


msg.setAsynchronous(true);


}


return queue.enqueueMessage(msg, uptimeMillis);


}


这段代码做了两件事:


1、给 msg.target 赋值,也就是 Handler 对象


2、给消息设置是否是异步消息。


3、调用 MessageQueue 的 enqueueMessage(msg, uptimeMillis)方法


我们只关注第三步:这一步把 Handler 的发送消息转给了 MessageQueue 的添加消息的方法。


所以至此,Handler 发送消息的任务也已经完成了,本质上就是调用 MessageQueue 自己的添加消息的方法!

[](

)三、MessageQueue 添加消息


MessageQueue 的构造函数代码如下:


MessageQueue(boolean quitAllowed) {


mQuitAllowed = quitAllowed;


mPtr = nativeInit();


}


也没做什么特别的事情。我们去看看 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;


}


代码很长,但是通过观察这段代码我们发现这个 MessageQueue 实际上是个链表,添加消息的过程实际上是一个单链表的插入过程。


所以我们知道了 Handler 发送消息的本质其实是把消息添加到 MessageQueue 中,而 MessageQueue 其实是一个单链表,添加消息的本质是单链表的插入

[](

)四、从消息队列里取出消息


我们已经知道消息如何存储的了,我们还需要知道消息是如何取出的。


所以我们要看一下 Looper.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;


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


}


}


}


代码太长我删了部分代码。可以看出这个方法主要的功能是很简单的。


获取 Looper 对象,如果为空,抛异常。


获取消息队列 MessageQueue queue


遍历循环从消息队列里取出消息,当消息为空时,循环结束,消息不为空时,分发出去!


但是实际上当没有消息的时候 queue.next()方法会被阻塞,并标记 mBlocked 为 true,并不会立刻返回 null。而这个方法阻塞的原因是 nativePollOnce(ptr, nextPollTimeoutMillis);方法阻塞。阻塞就是为了等待有消息的到来。那如果在有消息加入队列,loop()方法是如何继续取消息呢?


这得看消息加入队列的时候有什么操作,我们去看刚才的 enqueueMessage(msg, uptimeMillis)方法,发现


if (needWake) {


nativeWake(mPtr);


}


当 needWake 的时候会调用一个本地方法唤醒读取消息。


所以这里看一下消息分发出去之后做了什么?


msg.target.dispatchMessage(msg);


上面讲过这个 target 其实就是个 handler。所以我们取 handler 里面看一下这个方法代码


public void dispatchMessage(Message msg) {


if (msg.callback != null) {


handleCallback(msg);


} else {


if (mCallback != null) {


if (mCallback.handleMessage(msg)) {


return;


}


}


handleMessage(msg);


}


}


代码非常简单,当 callback 不为空的时候调用 callback 的 handleMessage(msg)方法,当 callback 为空的时候调用自己的 handleMessage(msg)。一般情况下我们不会传入 callback,而是直接复写 Handler 的 handleMessage(msg)方法来处理我们的消息。

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android进阶:用最详细的方式解析Android消息机制的源码