Android中,线程间通信最常见的方式是使用Handler消息机制。Handler 和 Looper、Message、MessageQueue 共同协作组成一个完整的Handler消息机制。下面我们通过源码来看看Handler是如何完成消息的收发的。
常见用法
先来看一下常见用法,然后跟着用法一步步在阅读源码。 1、初始化一个Handler对象
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
}
};
2、发送消息
//发送一个空消息
mHandler.sendEmptyMessage(1);
源码阅读
Handler 构造方法
第一步就是初始化一个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();
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;
}
从上面构造方法可以看到,我们在构造方法充初始化了mLooper 、mQueue 、mCallback 对象。
并且 mLooper 必须初始化,否则会报 RuntimeException 异常。还有后面的 mQueue 也是通过Looper获
得的,那么我们来看一下 Looper 初始化方法吧 mLooper = Looper.myLooper();
初始化 Looper 对象
查看Looper源码,可以看到官方的一个Looper 配合Handler 使用的例子。
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()方法中创建一个 Looper 对象并保存在 sThreadLocal 中,当我们多次调用 prepare()方法创建Looper 对象时,会抛出 RuntimeException 异常,保证每个线程中只有一个 Looper对象。
Looper 的构造方法如下:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
- 在构造方法中创建一个MessageQueue 对象。因为 Looper的唯一性,所以 MessageQueue在一个线程中也是唯一的。
- 获得创建 Looper 的线程,将Looper和线程进行绑定
2、Looper.loop()
上面通过 Looper.prepare() 创建并保存了一个 Looper 对象,同时创建了一个MessageQueue 对象。下面我们来看一下 Looper.loop() 方法的源码
public static void loop() {
final Looper me = myLooper();
//必须先调用 Looper.prepare() 初始化 Looper对象,才能调用loop方法
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//调用Looper.prepare() 时,在Looper 的构造方法中创建了一个mQueue 实例
final MessageQueue queue = me.mQueue;
//无限循环不停获取 MessageQueue 中的消息
for (;;) {
//调用 MessageQueue 的 next()方法获得 Message 对象
Message msg = queue.next(); // might block
if (msg == null) {
// 当 Message 为空时 阻塞等待
return;
}
...//省略部分
try {
//当有消息时,调用msg的target(Handler对象)的dispatchMessage()方法分发消息
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//消息分发完成后,回收消息内容
msg.recycleUnchecked();
}
}
从源码可以看到,loop()方法主要做了以下3件事:
- 校验 Looper 对象是否初始化,即有没有执行 Looper.prepare() 方法 。如果没有则抛出 RuntimeException
- 实现一个无限循环,不断获取 MessageQueue 中的消息,当消息为空时阻塞,当消息不为空时,调用handler的dispatchMessage() 方法分发消息
- 回收分发的消息内容
Looper中还有个prepareMainLooper()方法,是专门给主线程初始化使用的,一会就会用到。
Looper中的主要方法和作用就这些,说了半天好像我们第一部分常见用法并没有调用 Looper.prepare() 和 Looper.loop() 。
其实在我们的Activity的创建的时候Activity中已经为我们初始化好了一个Looper对象。
在ActivityThread 中初始化Looper
其实我们的Activity中也有handler线程切换,比如常用的 runOnUiThread 就是使用handler进行线程切换的。所以Activity早已经为我们在主线程中初始化好了一个Looper对象
在ActivityThread中的main()方法中初始化Looper
RuntimeException
异常
发送消息
sendMessageAtTime
Handler 和 Looper初始化完成后,我们就可以发送消息了。发送消息的源码如下:
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);
}
其实不管我们发送空消息或者非空消息,延时或者不延时发送,最终都会调用上面的sendMessageAtTime()
方法。 mQueue 在Handler构造方法中已经初始化完成,下面我们看一下 enqueueMessage()
方法源码
handler的enqueueMessage
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//将Handler赋值msg中的target对象。
//上面Looper获得消息后,会调用msg.target.dispatchMessage()分发消息
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//调用 MessageQueue.enqueueMessage方法将消息入队列
return queue.enqueueMessage(msg, uptimeMillis);
}
enqueueMessage方法主要做了两个件事:
- 将此handler对象赋值给Message中的target。后面会调用target.dispatchMessage()分发消息
- 调用MessageQueue的入队方法,将消息放入消息队列
下面看一下 MessageQueue的入队列方法enqueueMessage
MessageQueue.enqueueMessage()入队列
boolean enqueueMessage(Message msg, long when) {
//handler对象不能为空
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//是否已经被使用
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
//是否正在退出
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
//mMessages 队尾结点,将队尾结点赋值给P
Message p = mMessages;
boolean needWake;
//当消息队列为空时
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
//消息队列为空时,直接将msg赋值给队尾结点 mMessages
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
//当消息队列不为空时,记录上一个结点的消息
Message prev;
for (;;) {
//上一个消息
Message prev;
for (;;) {
//将队尾结点赋给上一个结点
prev = p;
//将队尾向后移动一位
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
//将新消息下一个结点指向队尾
msg.next = p; // invariant: p == prev.next
//上一个消息的下一个结点指向新消息
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
enqueueMessage方法其实就是一个队列的入队操作。当消息入队完成后,MessageQueue 就有了消息,同时在我们的Looper.loop()的无限循环方法中就能获得一个不为空的Message对象,然后调用msg.target.dispatchMessage(msg);
分发数据
dispatchMessage()数据分发
说了这么多次的dispatchMessage方法,现在终于到了查看dispatchMessage的源码的时候了,在handler中很靠前的方法。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
从源码可以看出当 msg.callback和 mCallback 为Null的时候,会调用 handleMessage()方法。很明显到目前为止我们并没有为msg.callback 和 mCallback 赋值,所以会调用 handleMessage()方法。同时我们在Activity创建Handler的时候已经覆写了handleMessage()方法,这样我们就可以在handleMessage方法中接收消息了。
handleMessage
mHandler.post()
mHandler.post(new Runnable() {
@Override
public void run() {
}
});
Handler还有一种post用法,源码如下:
sendMessageAtTime上面已经说过了,消息入队列,loop循环中发送消息,调用msg.target.dispatchMessage()分发消息。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
此时 msg.callback 不为空,执行handleCallback()方法,源码如下:
private static void handleCallback(Message message) {
message.callback.run();
}
然后在我们new的Runnable中收到回调。
Handler机制执行流程图
结束
到这里Handler机制相关源码就结束了,还好前段时间看了数据结构,又正好看到队列了,这里就遇到消息队列了☺。虽然阅读的有点晚,但只能慢慢赶上了。加油!