太心塞了,刚写了好几个小时总结好的,准备发表时候因为CSDN页面突然崩掉,搞得之前写了两个多小时的东西突然就没了,这倒是给了自己一个教训,以后写文章还是得用工具写,目前用的印象笔记,使用马克飞象支持MarkDown,也还是蛮好用的,废话不多说了,又得重新写一遍了,心塞塞
之前写过一篇对于源码的分析,有兴趣的可以看看Handler源码分析,现在这篇文章主要是为了自己总结好语言,方便面试的时候能说的通,不然面试的时候总是很含糊。
Handler消息机制是由Handler、Looper、MessageQueue、Message等组成的,它一般用来解决子线程中无法更新UI的问题(子线程中更新UI不一定会有问题,Android中子线程真的不能更新UI吗?),线程默认没有Looper的,如果需要使用Handler就必须为线程创建Looper。我们经常提到的主线程,也叫UI线程,它就是ActivityThread,ActivityThread被创建时就会初始化Looper,这也是在主线程中默认可以使用Handler的原因。所以,Handler机制的前提是要先有Looper,那么我们就先从Looper看起,看看Looper是如何创建的,创建时有什么特征?
一、Looper初始化
Looper初始化只能通过Looper.prepare()
方法进行初始化,在Looper.prepare()方法中,首先会通过ThreadLocal
的get()
方法来获取一个Looper对象,如果获取到的值为null,则会new一个Looper实例并通过ThreadLocal
的set()
方法来将该实例对象存储在当前线程内部,否则会抛出异常,这也表示在一个线程中Looper.prepare()方法只能执行一次,即也只存在一个Looper。 看下源码:
private static void prepare(boolean quitAllowed) {
//ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据
//数据存储以后,只有在指定线程中可以获取到存储的数据,对于其它线程来说无法获取到数据
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
二、MessageQueue初始化
通过上面我们知道了一个线程中最多只存在一个唯一的Looper对象,且每个线程中需要调用Looper.prepare()方法才会有实例化Looper对象(主线程在ActivityThread的main()方法中自动帮我们调用了Looper.prepare()方法),那么MessageQueue是在哪里初始化的呢?我们先看下Looper的构造函数:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
答案显而易见了,MessageQueue是在Looper的构造函数中创建的,前面说了,在一个线程中最多只存在一个Looper,那么同样的,一个线程中最多只存在一个MessageQueue。且由于主线程中系统已经自动为我们调用了Looper.prepare()方法,所以在主线程中一定存在一个唯一的Looper与MessageQueue。
三、Handler初始化
我们先看一下Handler的构造函数源码:
public Handler(Callback callback, boolean async) {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
在Handler构造函数中我们通过ThreadLocal.get()
方法获取到了当前线程的Looper与MessageQueue(假如当前线程调用了Looper.preapare()方法),这样Handler中就有了当前线程的Looper、MessageQueue。
四、Handler.sendMessage(msg)
通过前面的分析我们知道了在Handler初始化的时候获取到了当前线程的Looper与MessageQueue,那么现在我们假设在主线程中创建了Handler:
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {//主线程中执行
super.handleMessage(msg);
}
};
然后在子线程中发送了一条Message:
new Thread(new Runnable() {
@Override
public void run() {
mHandler.sendMessage(new Message());
}
}).start();
我们知道在子线程中发送的Message会在handleMessage(Message msg)
方法中回调,但是其具体是如何实现的,这才是我们要分析的:
1. 首先我们先看mHandler.sendMessage(new Message());
,该方法最终会将自身(Handler)赋值给了Message.target,然后调用MessageQueue的enqueueMessage(msg, uptimeMillis)
方法用来将Message发送到MessageQueue中。这样就将Message插入到了MessageQueue中。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
既然Message被插入到了MessageQueue中,那么又是如何被Handler回调处理的呢?这里就涉及到了
Looper.loop()
方法,Looper.loop()方法通常是与Looper.prepare()方法成对出现的,该方法源码较长,就不贴了。其主要的实现逻辑为:通过在一个无限循环的for循环中调用MessageQueue.next()
方法获取MessageQueue中的每一条Message,接着调用了Message.target.dispatchMessage(msg);
方法,前面我们说了在sendMessage()
时将Handler赋值给了Message.target
,所以,这里的Message.target.dispatchMessage(msg);
调用的就是我们Handler.dispatchMessage(msg)
方法,这样消息就回调到了我们的Handler中那么现在我们来看看
Handler.dispatchMessage(msg)
方法中做了什么处理:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
这就一目了然了吧!虽然前面有一些判断,但是我们还是可以看到其中有调用到我们的handleMessage(msg)
方法,这就实现了在子线程中发送的Message在主线程中回调的一个过程。至于前面有一些判断是用来处理Handler.post()更新UI时用到的,具体可以看前面的源码篇。
最后关于Handler的一些扩展介绍:Android 消息机制——你真的了解Handler?