Handler机制算是个老生常谈的问题了,最近又看了下源码,决定还是形成文字记录下来靠谱。
1、handler
handler在Android用于不同线程间的通信。使用时,一般会在主线程(接收消息的线程)new一个handler,那就先看下handler的源码:
public Handler(Callback callback, boolean async) {
...
//获取Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
从该构造方法可以得到以下几个信息:
1、Handler对象依赖于Looper对象。
2、Looper对象中当中持有mQueue对象。
3、Looper中的mQueue对象赋给了Handler成员变量。
3、Looper.prepare()用来创建Looper对象,有了Looper对象后才能创建Handler。
这里引出了Looper这个概念,让我们看下Looper是什么吧。
2、Looper
Looper的创建,即上面提到的Looper.prepare()方法:
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));
}
Looper的获取:
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static Looper myLooper() {
return sThreadLocal.get();//从ThreadLocal中获取
}
Looper的构造方法:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
该构造函数为私有的,也就是说Looper只能由它的静态方法prepare创建。构造方法内创建了MessageQueue对象。
那么Looper又是什么呢,它有啥用?看下源码的注释:
Class used to run a message loop for a thread. Threads by default do
not have a message loop associated with them; to create one, call
{@link #prepare} in the thread that is to run the loop, and then
{@link #loop} to have it process messages until the loop is stopped.
简单的说明下Looper是用来和Thread绑定的,它的内部维护了一个消息队列mQueue,所有发送给Thread的消息都会进入这个队列,然后Looper会循环处理这些消息。
看到这里,我们会有几个疑问:
1、Looper的创建和获取方法中引入了sThreadLocal,这是什么?
2、Looper如何与Thread绑定的?
3、Looper是如何接收消息,如何将消息存入它内部的消息队列mQueue的?
4、Looper是如何处理消息的?
下面就带着这几个疑问,依次解决。
3、ThreadLocal
首先讲下handler的作用,当然相信大家都懂得:假设有两个线程MainThread和SubThread。SubThread中通过获取在MainThread中创建的Handler对象,调用Handler的post或sendMessage方法,将消息传递给MainThread后并在该线程中处理。
由上面对Looper的介绍,我们又知道,实质上,发送的消息,最终交给了与MainThread绑定的Looper对象中的mQueue队列。最终的消息处理也是由这个Looper调度的。
如果让我们自己设计这样一个功能,不难想到,如果一个Looper绑定了多个Thread,那么Looper在接收到消息后处理,该在哪个线程中执行呢?所以每个Looper对象应只绑定一个线程,即每个Looper都是被某个线程独占的。
事实上源码就是这么设计,且每个Thread中只有一个Looper对象。
源码中为了保证每个Thread都有它独立的Looper,采用了ThreadLocal。
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).
Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the ThreadLocal instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).
用中文概括下来讲,就是对于同一类型的对象传给ThreadLocal后,ThreadLocal提供了一个绑定策略,它会将该对象绑定至当前的线程中,最终使得每个Thread中该类型的对象都是独立的,互不影响。
如果不太了解的话,可以参考这篇文章:https://www.jianshu.com/p/95291228aff7,看下文章开头的示例大概就明白了。
用在这里,就是ThreadLocal能够让每个Thread中都拥有独立的Looper对象。ThreadLocal是怎样实现这一魔法的,看源码:
Looper内持有静态且final的sThreadLocal成员变量。
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
在prepare方法中,创建新的Looper对象,并传给sThreadLocal。
sThreadLocal.set(new Looper(quitAllowed));
ThreadLocal中:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
//this,本类对象,如果结合Looper源码,这里的this就是指sThreadLocal。
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
从源码可以看出,set方法内首先获取当前Thread内的ThreadLocalMap,如果不存在,就先创建。到Thread源码内验证一下,果然有个ThreadLocal.ThreadLocalMap类型的threadLocals 成员变量。从名字xxxMap可以看出,这个成员变量是以键值对形式存储数据的。
结合Looper源码,key即为sThreadLocal,value即为Looper对象。这样,对于每个Thread线程,就都会有它独立的Looper对象了。
到这里,上面提到的四个问题中1和2就解决了。
4、MessageQueue
MessageQueue是在Looper的构造方法内初始化的,Thread和Looper是唯一绑定的,所以每个Thread也只能有唯一的一个MessageQueue!MessageQueue是链表解构的队列,因为消息需要频繁的增删,所以采用链表效率更高。
当我们调用Handler的sendMessage或post发送一个消息时,最终都会调用:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这里的queue就是在Looper构造函数内初始化的那个。
消息存入该链表后,Looper的loop方法就会循环遍历这个链表,一次取出消息,最后交给Handler的handleMessage取处理:
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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
//msg.target就是发送消息时的那个handler。
//最终调用handler的handleMessage方法处理。
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
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();
}
}
Looper的loop方法,会在创建Looper(Handler)的那个线程中调用,该方法中最关键的是msg.target.dispatchMessage(msg);
,target从何而来?看下Handler的enqueueMessage方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
原来msg.target就是发送消息的那个Handler本身!事件回传给Handler的dispatchMessage处理,贴出源码:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
是不是熟悉的方法来了!如果msg是runnable事件,调用handleCallback(msg)
,最终调用runnable的run方法执行;
如果msg是普通事件,就调用 handleMessage(msg)
执行,而handleMessage不就是我们通常会重写的handler的方法吗?
同时,因为loop方法在创建Looper的线程中执行,那么loop方法中的所有方法调用,都未涉及到线程切换,所以也都在Looper创建的线程中执行。
5、画个图吧
想了想还是来个图直观点!
6、总结
Handler这一套机制,说白了也挺简单的。
1、首先需要调用Looper.prepare(),为当前线程创建Looper对象,同时创建的还有MessageQueue,ThreadLocal会用来保证每个线程拥有独立的一套Looper和MessageQueue。
2、然后再new Handler(),它会持有刚才在线程中创建的Looper和MessageQueue对象,从而与线程间接关联。(注意,一个Handler对应一个线程,但一个线程中可以有多个Handler,但多个handler多会持有同一个Looper和MessageQueue对象)。
3、最后在同样的线程中调用Looper.loop()循环遍历MessageQueue链表,该循环是一个阻塞方法,它会一直执行,当消息到来,就会依次取出消息,让后让发送该消息的那个handler处理事件(具体怎么处理取决于我们重写的Handler的handleMessage方法)。由于Looper.loop()方法是在创建Looper的那个线程中调用的,所以handleMessage也会在同一线程中执行。
4、Handler的异步消息处理机制的关键在于:Handler对象对每个线程都是可见的,在不同线程中,可以拿到这个handler发送消息;但Handler对应的Looper和MessageQueue却是每个线程独立存在的;消息最终会在创建Looper的那个线程中被处理,从而达到异步处理的目的。