Android的消息处理机制有如下主要类实现:Message, MessageQueue, Looper, Handler。读完这篇文章能对这几个核心类的职责和功能有所了解,对这几个类之间的相互依赖关系有进一步的认识。Hope Help!
1.核心类的职责和部分源码分析
a. Message:消息对象包含一个描述和任意数据对象,该对象可以被发送给Handler。同时,又额外定义了两个int字段和一个对象字段,使用这些已定义的字段就可以满足我们大部分场景的需求。
- Message内部使用了享元模式,维护了一个对象池,所以有追求的我们肯定要使用Message.obtain()来获取消息
- Message对象有一个callback字段,是Runnable类型, 如果有设置,将优先执行该callback。来看看Handler中的dispatchMessage()。优先由Msg中的callback来处理,Handler中的mCallback次之,最后才是我们最熟悉的Handler中的handleMessage()
/**
* 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);
}
b. MessageQueue:底层类,表示一个消息队列。名为消息队列,该类中并没有一个列表或者队列的定义,而是通过Message对象来实现的。因为Message成员变量中包含了一个next字段,类型为Message,实现了一个链表。该类主要有两个职责:
一是负责消息入队列,见enqueueMessage(Message msg, long when)
boolean enqueueMessage(Message msg, long when) {
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) {
...
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 {
/*
*这里一开始很疑惑,明明每次enqueueMessage都有个msg.target为null的判断,为什么底下还有个p.target == null的判断呢?
*原来MessageQueue里还有一个syncBarrier机制,详见postSyncBarrier(), 在那里构建的msg不指定target
*/
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
//neekWake为true意味着上面的三个条件都成立,有syncBarrier的作用同时消息为异步消息,这时又将needWake置为false,
//表明不需要唤醒底层的管道通信,这映证了syncBarrier除了带有消息优先级外(优先处理异步消息),
//还会拖延、阻碍同步消息的处理。
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;
}
二是负责获取下一条已准备好待处理的消息,详见next()。这里就不看源码了,主要就是在for构造的无线循环内部调用native方法nativePollOnce(ptr, nextPollTimeoutMillis)去取消息,有就读取缓存并获取消息,没有就会阻塞。
c. Looper:线程默认是不存在消息循环的,但是通过在线程内部调用Looper.prepare(),接着调用Looper.loop()制造了一个消息循环。使得线程可以一直处于运行状态直到循环退出。
我们来看一个典型的Looper Thread的例子:
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();
}
}
- 第一步:在线程的run()先调用了Looper.prepare(),代码实现很简单,通过线程本地变量ThreadLocal保证每个线程只能创建一个Looper。
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));
}
第二步:在run()结束前调用Looper.loop()。从源码可见,loop()实则是运行了一个无限的for循环,每次循环会从消息队列中获取一个消息,然后进行消息的分发处理。
虽然是无限循环,但我们不用担心其性能。因为queue.next()在获取不到消息时会进入空闲等待,并不会占用CPU资源。在很早之前学习Android的消息机制时,对Looper把线程变成一个无限循环的线程感到很神奇。不过从源码中得知,不过是Looper在线程里执行了一个运行周期很长的任务而已,并没有改变线程的性质。看来还是要多读源码长见识。
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;
}
...
try {
msg.target.dispatchMessage(msg);
} finally {
}
...
msg.recycleUnchecked(); //回收msg对象
}
}
d. Handler:允许我们发送和处理Message以及Runnable对象。当我们创建一个新的Handler,它就和线程以及消息队列产生了关联。Handler会将Message和Runnable对象发送到绑定的消息队列中并按照它们出队列的顺序来执行。
从下面Handler的构造方法我们可以看出,Handler和线程,以及消息队列是如何关联起来的了。在这个构造方法中,调用了Looper.myLooper()获取Looper对象,Looper对象在实例化时就与当前线程关联起来了。又从Looper中获取到了消息队列赋值给了Handler的mQueue。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
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;
}
2.核心类依赖关系
画图之前正好又温习了一遍UML的6种关系。然后在网上又找了一遍消息机制核心类的UML类图,没找到合适的,或者说看到了好多个版本,但跟自己的认知有一定偏差,于是就凭着自己的理解画了一个。
我把Looper、MessageQueue以及Thread之间的关系定义为组合关系,因为我的理解,这三个可以独立存在,但我觉得如果分开那就不是消息循环了。可能有些人会觉得这三者之间的关系用聚合来表示更合适,但个人更倾向组合,因为缺一不可,不可分割。不过这带有个人的主观想法在里面。
3.总结
通过本篇文章,主要有以下收获: