前言
关于异步消息的基础用法,在子线程发送消息到主线程,可以看之前的一篇文章http://blog.youkuaiyun.com/leelit/article/details/45196827,而现在这篇文章主要是从源码角度来解析这套异步机制。
Handler经典用法
class MyThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
Handler官方文档的使用教程就是上面这个例子,我也一直认为学习Handler不能只看子线程发送消息给主线程这个最常用的场景。
通过上述步骤,我们便可以在其他线程,通过Handler来发送消息给当前线程MyThread,使得想要进行的任务在该线程执行,达到异步效果。run方法里一共就三步,接下来我们一步步来分析。注:本文后续所有的当前线程,都是指MyThread。
流程
1、Looper.prepare();
public static void prepare() {
prepare(true);
}
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));
}
prepare方法给ThreadLocal对象set了一个Looper对象,由Looper类的静态成员变量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
可知,
每条线程关联一个Looper,当然如果没有在run方法里调用prepare方法,该线程就无法通过Looper类的sThreadLocal
get到实例。
而Looper最核心的点就是组合一个消息队列对象
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
小结第一步:调用prepare方法,当前线程关联一个Looper对象,Looper对象组合消息队列对象。
2、mHandler = new Handler() {...};
public Handler() {
this(null, false);
}
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;
mCallback = callback;
}
第二步实例化Handler对象,Handler对象组合一个Looper对象,取得这个Looper对象的消息队列,后续Handler可以发送消息到该消息队列。
3、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);
...
}
}
当前线程死循环轮询,Looper对象不断从消息队列里面拉取Message,并且是阻塞式的。假设拉到了Message,则调用:
msg.target.dispatchMessage(msg);
将消息回调给msg.target,而msg.target正是Handler对象,dispatchMessage分发方式对应三种使用Handler的方法。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg); // Handler.post
} else {
if (mCallback != null) { // 实例Handler时传入的Callback参数
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg); // 实例Handler时重写的handleMessage方法
}
}
小结第三步:当前线程死循环轮询消息队列,一旦取得消息,在当前线程执行Handler回调方法。
总结
盗图一张,from《第一行代码》
(1)Looper对象在某条线程里死循环轮询消息队列;
(2)Handler组合Looper对象,取得其消息队列;
(3)Handler发送消息到消息队列,Looper在队列里取得消息后在该线程执行Handler回调。
最后还有一个问题,Handler实例时可以直接传入Looper参数,这样回调便发生在该Looper关联的线程,比如最常见的发送消息到主线程:
new Handler(Looper.getMainLooper()).post(...);
如果不传入Looper参数,则默认使用当前线程的Looper,所以在某条线程实例化Handler前必须先调用Looper.prepare
,使得该线程关联有Looper对象,并且还需要调用Looper.loop
(主线程也是有这两个动作的,在ActivityThread这个程序入口类的main方法里),否则即使通过Handler发送了消息Looper也不会取出来进行回调处理。
用HandlerThread实现消息队列
假如我们想要实现自己的一套消息队列来处理特定业务,咋整呢,Google已经帮我们做了所有工作了,直接使用HandlerThread即可。
HandlerThread部分源码
public class HandlerThread extends Thread {
Looper mLooper;
public HandlerThread(String name) {
super(name);
}
protected void onLooperPrepared() {
}
@Override
public void run() {
...
Looper.prepare(); // 这条线程关联Looper
mLooper = Looper.myLooper(); // 这条线程的Looper
Looper.loop(); // 让这条线程的Looper开始轮询消息队列
...
}
}
我们只需要做下面的动作:
public static Handler myBusinessHandler;
public static void method(){
HandlerThread thread = new HandlerThread();
thread.start();
myBusinessHandler = new Handler(thread.getLooper());
}
调用完上面这个方法后,就可以用myBusinessHandler来发送消息了,然后Looper会在这条线程里回调你的任务,就是这么简单^_^