Handler 异步消息处理机制
- 我们先来看一下一个标准的异步消息处理机制是如何写的.
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
以上代码摘自官方文档.
根据代码分析
Looper
- 在创建Handler之前,必须先调用Looper.prepare()方法, 等等, 我们在UI线程使用Handler的时候并没有调用过这个方法啊! 是的, 这是因为在UI线程中已经调用了Looper.prepare()和Looper.loop()方法.
- 现在一步一步来看看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));//创建了一个Looper对象
}
- 再看看Looper的构造方法:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); //创建了一个消息队列
mThread = Thread.currentThread();
}
可以看到, 主要是调用了sThreadLocal.set()方法, 创建了一个Looper对象, 这里简单介绍一下ThreadLoacl是个什么东西,这里不作深入讨论:
- 是一个线程内部的数据存储类.
- 通过它可以在指定的线程中存储数据, 并且只有在该指定线程中获取到存储的数据.
- 对其他线程来说则无法获取到数据.
通过以上代码我们知道:
因为Looper.prepare只能调用一次, 所以一个线程只维护一个Looper对象和一个MessageQueue消息队列. 在这写准备好之后, 我们来看一下上面标准代码中的下一行, 创建Handler对象.
Handler
- 还是从构造方法看起:
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper(); //注意这里, 获取了一个Looper对象
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue; //获取这个Looper对象中保存的MessageQueue
mCallback = callback;
mAsynchronous = async;
}
- 我们来看一下myLooper的方法:
public static @Nullable Looper myLooper() {
return sThreadLocal.get(); //从当前线程获取保存的Looper对象
}
- 通过以上代码我们知道了handler的对象与我们Looper对象中MessageQueue是如何关联上的.
- Looper对象有了, MessageQueue有了, Handler也有了, 我们先来回顾一下Handler是如何发送消息的:
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message(); //创建一条消息
message.arg1 = 1;
Bundle bundle = new Bundle();
bundle.putString("data", "data"); //bundle封装消息
message.setData(bundle); //消息保存数据
handler.sendMessage(message); //定义在主线程的Handler在子线程发送消息
}
}).start();
- 好, 我们先来看一下sendMessage到底做了什么.
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis); //这个方法毫无疑问就是入队的方法
}
- Handler中提供了很多个发送消息的方法,我们在发送消息的时候除了sendMessageAtFrontOfQueue()方法之外,其它的发送消息方法最终都会辗转调用到sendMessageAtTime()方法中, 这个方法最终会调用Handler的enqueueMessage方法.
- 我们再来看看enqueueMessage做了哪些事:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; //把当前的handler赋值为msg的target属性
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
- enqueueMessage最终会调用queue的enqueueMessage的方法,也就是说handler发出的消息,最终会保存到消息队列中去。
- 既然有入队, 肯定就有出队, 我们最后来看一下一开始的标准代码的最后一句Looper.loop()方法.
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;
// 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 不断从MessageQueue的对象中读取消息,即出队
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
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);//每当有一个消息出队,就将它传递到该方法,msg.target即是前面赋值的Handler对象
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();
}
}
- 看完上面的注释, 我们来看看Handler的dispatchMessage做了什么:
public void handleMessage(Message msg) {
}
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg); //是不是很熟悉! 我们重写的handleMessage是在这里回调的!
}
}
最后
- 总结一下:
- Looper主要的作用是通过loop()方法建立一个无限循环体, 在不断的从MessageQueue中去取消息,交给消息的target属性(即Handler对象)的dispatchMessage去回调handleMessage方法.
- Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。
- Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。
- 感谢郭神和鸿洋的详细讲解, 本文很多内容就是来源于他们的博客, 最后贴张图,来自鸿洋大神:Android 异步消息处理机制