续上篇
Android Handler之从主线程往子线程发消息(一)
简单回顾一下Handler机制中几个对象的主要作用
Handler机制中最重要的四个对象
Handler:负责发送消息及处理消息
Looper:复制不断的从消息队列中取出消息,并且给发送本条消息的Handler
MessageQueue:负责存储消息
Message:消息本身,负责携带数据
那么,一个消息从发送出去,到回到Handler自己身上,这个过程具体是怎样的?
这个就不得不去看源码了
我们从Android Handler之从主线程往子线程发消息(一)中的
四、怎么从主线程发送消息到子线程?(虽然这种应用场景很少)
的示例代码看起
Thread thread = new Thread(){
@Override
public void run() {
super.run();
//初始化Looper,一定要写在Handler初始化之前
Looper.prepare();
//在子线程内部初始化handler即可,发送消息的代码可在主线程任意地方发送
handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//所有的事情处理完成后要退出looper,即终止Looper循环
//这两个方法都可以,有关这两个方法的区别自行寻找答案
handler.getLooper().quit();
handler.getLooper().quitSafely();
}
};
//启动Looper循环,否则Handler无法收到消息
Looper.loop();
}
};
thread.start();
//在主线程中发送消息
handler.sendMessage();
选择这个蹩脚的demo是为了让大家更好的理解Handler机制,耐心看下去,最后你心中所有的疑问都会有答案
一、先来解释第一行代码
Looper.prepare();
这个很好解释,只要查看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;
mAsynchronous = async;
}
看到了没有,Handler的构造方法中会验证Looper,如果Looper为空,那么会抛出空指针异常
如果你足够细心,你会发现,Handler在构造方法中还做了一件事,
将自己的一个全局消息队列对象(mQueue)指向了Looper中的消息队列
即构造方法中的这行代码
mQueue = mLooper.mQueue;
先记住,有用
二、第二行代码是初始化了Hanlder并且重写HandleMessage()方法
这个没什么好解释的
三、我们调用了
Looper.loop()
方法,这个方法的具体原理随后解释
四、handler.sendMessage(message)的主要作用
最接近我们使用的就是handler.sendMessage(message);
这行代码了,那么我们从这行代码看起,看看这行代码之后发生了什么,为了方便大家看到,我把代码执行流程画了出来
红线画出来的代表代码是哪个类的,可以看到,我们sendMessage()之后代码通过图中所示的几个方法,最终执行到了MessageQueue的enqueueMessage()方法。也就是说,接下来我们要看的就是MessageQueue中的方法了。
上源码
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
msg.markInUse();
msg.when = when;
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;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
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;
}
为了方便阅读,我删减了部分代码
可以看到,MessageQueue是一个单向列表结构,而MessageQueue的enqueueMessage()方法主要做的事情就是将Handler发送过来的Message插入到列表中。
也就是说,当我们调用handler.senMessage()方法的时候,最终的结果只是将这个消息插入到了消息队列中
上面的流程图中有一个问题:
最后一行代码queue.enqueueMessage()
中的queue对象是什么时候初始化的?
还记得本篇博客在解释Looper.prepare()方法部分的最后一段话吗?
不记得了就往上翻一翻
发送消息的工作已经完成,那么Looper是什么时候取的消息,取出来消息又是怎么送回给Handler的呢?现在我们就不得不看Looper.loop()
方法了
Looper.loop()方法
public static void loop() {
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
msg.target.dispatchMessage(msg);
}
}
}
同样的,为了降低阅读难度,我删掉了大部分的代码,只留下了这么几行核心代码,现在我们来看这几行代码的逻辑
- 这是一个死循环
- 这个循环的目的是从MessageQueue中取出消息
- 取消息的方法是MessageQueue.next()方法
- 取出消息后调用message.target对象的dispatchMessage()方法分发消息
- 循环跳出的条件是MessageQueue.next()方法返回了null
不过,看到这里我们应该自然会想到,message.target.是哪个对象?
该对象的dispatchMessage()方法都做了什么操作?
带着疑问,我们进入Message类中去寻找target
public final class Message implements Parcelable {
/*package*/ int flags;
/*package*/ long when;
/*package*/ Bundle data;
/*package*/ Handler target;
/*package*/ Runnable callback;
// sometimes we store linked lists of these things
/*package*/ Message next;
}
看到了吧,Message类中有一个成员变量 target,而这个target是一个Handler,也就是说,在Looper从MessageQueue中取出Message之后,调用了Handler的dispatchMessage()方法。
这里我们不禁要问,这个target指向了哪个Handler
带着这个疑问,我翻遍了Message类,也没有看到我想要的答案。此时我就想,既然Handler发送了消息就能接收到消息,那么会不会是在发送消息的途中发生了什么细节是我不知道的,那么我只好仔细看发送消息过程中的代码,终于让我发现了这个
//Handler的方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
在这个方法的第一行代码就是,将message的target属性赋值为发送message的handler自身。如果读者忘记了这个方法的调用时机,请往上翻翻,查看一下第四部分sendMeeage()中的流程图
也就是说,Looper取出消息后,调用了发送消息的Handler的dispatchMessage()方法,并且将message本身作为参数传了回去。到此时,代码的执行逻辑又回到了Handler中。
接着看handler的dispatchMessage()方法
/**
*handler的方法
* 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()方法,对,这个方法就是我们经常要重写的handleMessage()方法,也是我们处理消息时候的逻辑。
到这里,Handler机制工作的主要流程就完成了。
再来一个系统的工作示意图
如果读者认真看了博客,那么到这里就应该对Handler的基本工作流程比较清晰了。我也在前面详细讲解了流程图中的方法调用过程。不过还有一些问题我们没有处理
一个线程中最多可以有几个Looper和几个MessageQueue?
我们来看Looper的构造方法即初始化方法
//Looper暴露出的静态初始化方法
//这个方法会调用下面的私有静态方法
public static void prepare() {
prepare(true);
}
//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));
}
//私有的构造方法,禁止外界调用
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
//从sThreadLocal中获取一个Looper
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
解释一下上面三个方法
- 我们只能通过Looper.prepare()方法去初始化一个Looper
- Looper.prepare(boolean)方法的逻辑是一个线程中只能有一个Looper对象,否则在第二次尝试初始化Looper的时候,就会抛出异常。
- 以线程为单位存储Looper的主要逻辑是通过ThreadLocal实现的
- 私有的构造方法,禁止外界任意new出一个Looper
通过这段逻辑我们可以看出,
一个线程中最多有一个Looper
接着看Looper的构造方法,里面有一行代码
mQueue = new MessageQueue(quitAllowed);
是的,我们的MessageQueue是随着Looper的初始化而初始化的。那么,MessageQueue能不能随意的被new出来呢?
//MessageQueue的构造方法
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
可以看到,MessageQuede的构造方法是default的,也就是说,只有跟MessageQueue同一个包下才可以实例化MessageQueue,换句话说,我们用户是无法直接new一个MessageQueue对象出来的。而因为Looper在一个线程中只能有一个,从而导致MessageQueue也只能有一个
。
网友评论