从用法解析源码:
Handler重要性:
- 作为一个Android开发人员,只要一入Android这扇门,Handler就是一个伴随你开发终生的API。它的重要性不言而喻。但是我们虽然平时一直在用,却并没有真的研究过它的原理。
- 今天我们来剖析剖析,写一篇你能懂的解析。
用法:
- 万变不离其宗,我们首先还是聊一聊Handler的用法:
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
mHandler.sendEmptyMessageDelayed(what,time);
- 上面这段代码,使我们最常使用Handler的方式了。通过Handler对象发送消息,能够通知UI线程去做一些我们希望达成的事情。这是我们使用Handler最常用的地方(更新UI)。
从Handler构造器入手:
- Handler的构造有多个重载,我们看下代码:
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
...
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中取出消息队列
mCallback = callback; //设置Callback接口
...
}
- 从上面,我们引出了好几个概念:
- Looper(消息循环器)
- MessageQueue(消息队列)
- Message(消息)
- Callback(回调)
- 接下来我们来一个一个介绍他们
#Message
- Message对象就很简单了,我们总说Handler用来发消息,发送的就是Message对象。
- 它的开发中常用的what、obj等成员,我们就不细说了。不知道的同学,赶紧面壁思过去。
- 需要介绍的,就是Message对象的target、sPool:
##target
- target成员,其实就是Handler对象。
- 每一个Message对象,都保存一个Handler成员,这个taget,表示的就是这个Message到底从哪一个Handler来。
- 我们在来回顾下这段代码:
Message msg = Message.obtain();
msg.what = 1;
mHandler.sendMessage(msg);
- 为什么mHandler发送的消息,最终能够回调到Handler自身的handleMessage呢?
- 第一个原因,就是因为,Message对象持有了Handler引用,它知道它来自哪一个Handler。
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;//在Handler调用obtain获取Message对象的时候,Message对象的target就被赋值了。
return m;
}
- 如上,在我们调用Message对象的生成逻辑的时候,其实Handler对象就已经传递进来并且设置给了target进行保存。
- 这样就很清晰了,Message自己管理自己应该交给谁处理。这个设计,不得不说真的很巧妙。
- 当然有的同学说,Message的obtain()函数明明可以不传入Handler。稍安勿躁,先坐下,这位扁桃体同学,不要着急发炎,我们后面再说。
##Message的创建、回收
- sPool看名字就能知道是一个对象池。
- 但是sPool的声明又是Message自己。
- Message的模型设计是,用static sPool变量,链式管理对象池。
- 我们先看看Handler的obtain()函数:
public final Message obtainMessage()
{
return Message.obtain(this);
}
- Handler获取消息,其实调用的是Message的static函数obtain()
- 我们看看Message的obtain()
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;//首先看看sPool是否是null,不是的话将自己暂存
sPool = m.next;//然后把Message管理的next交给sPool,这样sPool就是队列里的下一个Message
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
注意看注释的两行,消息池的原理也很简单。每一个Message对象都管理着自己的链表下一个节点
因此,Message对象也是一个链表形式的对象池进行管理的。
那么问题来了,Message对象最多是多少个呢?
private static final int MAX_POOL_SIZE = 50;//50个
Message对象还有一个需要注意的回收函数
msg.recycleUnchecked();
我们来看一看Message的回收过程:
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {//判断是否已经到达对象池的最大值
next = sPool;//将sPool交给自己的next
sPool = this;//把自己作为sPool,这样在obtain()调用的时候就可以再次被利用了
sPoolSize++;
}
}
}
- 从这两段代码,基本可以知道,Message对象池的设计,其实就等于sPool是队头,每次取数据都是优先取队头的数据。而当obtain的数据过多的时候,又能不断新建对象作为缓冲。
- 当然我们针对于Message的对象复用,只需要知道原理就好。只要记得Message最重要的其实还是自己管理了自己的分发Handler对象,Handler的消息就能从出发找到源头了。
MessageQueue
- 顾名思义,它就是消息队列了。在Handler的构造中,我们能看到它是从Looper中取出来的。
- MessageQueue就是存放我们发送的消息的队列,Handler的消息循环器(Looper)就是从它身上不断地获取消息进行处理的。
- MessageQueue在整个Handler的体系中,只有一个,换句话说,每个线程只有一个MessageQueue。
- 消息队列,其实不外乎就两个操作,一个入队一个出队。我们先来看看消息入队的操作。
消息入队:
- 我们在使用Handler的时候,调用sendMessage等一系列的API,其实就是在发送消息进入MessageQueue队列。
- 不管消息使用什么方式发送,延迟也好,立刻发送也好,Handler的消息发送函数最终都会回调到以下函数:
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自己的MessageQueue成员,然后进行下一步操作。
- 而且发消息的操作,核心还是在enqueueMessage()函数中,我们来看看这个函数:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;//设置msg的回调target。这个target就是Handler本身
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);//果不其然,走到了MessageQueue的入队函数
}
- enqueueMessage有一个细节值得注意的。它在拿到Message对象的时候,把Handler的this指针,置给了Message对象的target成员。
- 这样,即使Message在obtain()的时候没有传入Handler,那消息进队列的时候,总会把Handler塞给target。刚才扁桃体发炎的同学,你可以消肿了 :D 。
- 记笔记,要考!!!
###进入MessageQueue
- 前面的调用过程都还是在Handler里,这下才真的进入到MessageQueue中。
- 再来看MessageQueue的入队函数:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
//如果消息没有target就报错,因为不知道将msg交给哪一个Handler处理
//所以重点还是Message自己管理自己的处理着(Handler)
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
//如果msg已经在使用,也报错。
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;//消息的确切处理时间
Message p = mMessages;//当前入队过的最后一条消息,注意不是队尾消息,因为可能是插入到队列中任何位置的
boolean needWake;//是否需要唤醒Looper,其实Looper不是永远死循环的,没消息的时候,也会阻塞,避免出现性能问题
if (p == null || when == 0 || when < p.when) {
//如果上一条消息为null,表示是新队列,msg作为队头
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;//是否要唤醒循环器
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//msg不在队头,就在队列中找到合适的位置
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
//将msg插入到链表中合适的位置
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
//如果需要唤醒,就调用native函数,唤醒阻塞的队列
nativeWake(mPtr);
}
}
return true;
}
- 整体代码看下来,基本可以清晰的知道Handler发出的消息最终是怎么进入到MessageQueue队列中的。
- 从上面还可以看到一个特殊的变量needWake,它最终决定了是否需要调用一个native函数nativeWake()
- 这里我们先简单介绍一下,这是一个唤醒消息循环器的机制,我们一会儿介绍Looper同学的时候,再让他出来发言。
小结
- 从Handler的sendMessage走到这里,消息从创建到进入MessageQueue,其实就是简单的为Message是哪一个Handler处理,以及队列的数据结构操作。
- 原理上来说,这部分还是比较简单的。
接下来
- 有了Message和MessageQueue,后续我们还要再看看Looper、Callback,具体看看消息是怎么被取出来,怎么被处理掉的。