1)Handler、Looper、MessageQueue
Handler成员变量有mLooper、mQueue,它们分别是Looper和MessageQueue的引用。在实例化Handler时,Handler通过Lopper.myLooper()来获取本线程的Looper实例,再从Looper中获取消息队列,mQueue = mLooper.mQueue。没错,消息队列是在Looper中存储的。
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
可以看到,Looper实例其实是从线程本地存储类ThreadLocal中获取到的。
这里只看到Looper实例从线程中获取,但是,是什么时候添加进线程中去的呢?
我们看下Android应用程序的主入口ActivityThread.main
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
Looper.loop();
查看Looper.prepareMainLooper的调用层次,最后调用到Looper.perpare方法,在此方法中就会新建一个Looper的实例,并且传入线程本地中
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));
}
另外Handler有send和post两种,send方法一般有一个Message类型的参数,封装了要发送的信息。post方法一般有一个Runnable类型的参数,并都会通过handler中的getPostMessage(Runnable r)封装成一个Message。这两个方法最后都会调用到sendMessageAtTime,在此方法中就会将发送的消息进入消息队列。
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
...
return enqueueMessage(queue, msg, uptimeMillis);
}
最后,通过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;
}
...
msg.target.dispatchMessage(msg);
...
}
}
msg.target.dispatchMessage(msg);target是Handler类型的,也就是说,一般一个Message也会持有一个发送它的Handler的引用。通过这个引用,调用Handler的dispatchMessage来分发消息
public void dispatchMessage(Message msg) {
//使用post发送时,msg封装了callback,在此处处理
if (msg.callback != null) {
handleCallback(msg);
} else {
//创建Handler时,如果注入了CallBack实例,在此处处理
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//这是我们一般会重写的方法
handleMessage(msg);
}
}
//下面是Callback接口的形式
public Handler(Callback callback) {
this(callback, false);
}
public interface Callback {
public boolean handleMessage(Message msg);
}
推荐阅读:带着这篇去通关所有Handler的提问、Handler带来的内存泄漏
2)在子线程中创建Handler为什么会抛出异常
回忆一下,在创建Handler实例时,要通过Looper.myLooper获取本线程的Looper,但是一开始,线程本地是没有Looper的。主线程可以直接创建Handler,是因为在程序入口处,调用Looper.prepareMainLooper在主线程加入了Looper,回顾一下,此方法最终调用Looper.prepare在线程本地添加Looper,所以要在子线程中创建Handler,可以在创建前,调用Looper.prepare,并且在调用后开启消息循环,Looper.loop。
3)ThreadLocal类
上面讲到Looper.prepare方法会在线程本地存入Looper对象,其实就是存到ThreadLocal对象里面了。
Looper相关:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
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));
}
下面是ThreadLocal的set方法:
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
可见ThreadLocal存储的键是当前线程对象,所以不同的线程就有不同的ThreadLocal,且不会发生同步等问题,这种设计让人焕然一新。
另外,在Looper类中,会一直持有UI线程的Looper对象,可以通过getMainLooper获得,用于与UI线程通信。
推荐阅读:
Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系、
Android Handler 异步消息处理机制的妙用 创建强大的图片加载类
图解消息循环机制