前言
在看过了网上那么多的Android的异步消息处理机制的文章之后,总是觉得不够系统,要么是copy来copy去的代码,要么是凌乱的结构,让人看的云里雾里的,也可能是没有看懂并转化成自己的东西。官方文档也没有详细的去解释其之间的关系,只有的类注释,然后我们就只能Read the Fucking Source Code !其实我们只要把官方文档的解释加上源码就能理清这个机制的原理。
PS:在此强调一点就是,去学习android的东西,一定要看官方文档,官方文档才是最原汁原味的,网上的文章都是别人消化过的,不是你的理解,所以一定要去官网都挨个撸一遍,做到心中有数,这样以后查起文档就有的放矢了。
1. Android的线程
在说异步消息处理机制之前,一定要先去看一下这篇文章《Android的单线程》,只有看了这篇文章你才知道为什么会有异步消息处理机制。
2. 异步消息处理机制类
首先我们要了了解一下这几个类:
- Handler
- Looper
- Message
- MessageQueue
在介绍之前,我想大家最一开始使用的都是从Handler,即使没看过源码,也知道大概的使用方式了,所以使用方法这里我不做赘述,下面一一介绍:
Handler
我按照官方文档英文翻译一下,如果大家英语好的话也可以去原文地址去看:
一个Handler允许你发送或处理Message和Runnable对象到线程的消息队。每一个Handler实例都和一个单一的线程相关联,就是消息队列所在的线程。当你创建一个新的Handler对象,从那时起,它就会绑定到线程所创建的消息队列中,它会发送消息或者Runnable接口到消息队列,当他们从消息队列出来之后便依次执行。
然后Handler有2种主要的使用:
- 执行Message对象和在未来特定时刻执行Runnable接口;
- sendMessage(Message)
- sendEmptyMessage(int)
- sendEmptyMessageDelayed(int,long)
- sendEmptyMessageAtTime(int)
- sendMessageDelayed(int,long)
- sendMessageAtTime(int,long)
- sendMessageAtFrontOfQueue(Message)
- 在一个不同的线程加入一个动作使之执行而不是你自己去实现。
- post(Runnable)
- postAtTime(Runnable,long)
- postAtTime(Runnable,object,long)
- postDelayed(Runnable,long)
- postAtFrontOfQueue(Runnable)
然后我们在Handler.handleMessage Callback中处理回传回来的消息。
Looper
Looper这个类大家肯定也很熟悉,还记得你第一次使用Looper,抛出RuntimeException :"Only one Looper may be created per thread"吗?没错就是你在某一个已经绑定了一个Looper对象的线程中调用Looper.prepare()时产生的。
下面看一段典型的代码:
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的作用其实就是一个消息循环器,它不停的从消息池中取出消息,然后在当前线程中分发消息。但是使用的时候一定要注意,要绑定到一个线程,先调用prepare再调用loop。
那么Looper.prepare()究竟做了什么,我们看源码:
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
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()的注释:
注释:Initialize the current thread as a looper.This gives you a chance to create handlers that then reference
this looper, before actually starting the loop. Be sure to call loop after calling this method, and end it by
calling quit翻译:初始化当前的线程作为一个looper对象。在真正的开始消息循环之前,给你机会去创建一个或多个handler来指向到这个looper对象。但是要确保当调用完这个方法之后一定要调用Loop方法,然后调用quit来结束它。
然后我们再看第二个函数的具体实现,我们看到了有一个类ThreadLocal貌似我们从未见过,也没有在实际项目当中使用过,但是如果有的同学如果看过《JAVA并发编程实战》第三章节中的对象的共享,应该会有印象。
不过没关系,我们还是用过类注释来了解它的作用:
注释:Implements a thread-local storage, that is, a variable for which each thread has its own value. All threads share the same ThreadLocal object, but each sees a different value when accessing it, and changes made by one thread do not affect the other threads. The implementation supports null values.
翻译:实现了一个线程局部存储,就是每一个线程都有自己对应的值。所有的线程共享ThreadLocal对象,但
是当访问的时候,每一个线程会看到不同的值。一个线程的改变不会影响其他线程。这种实现还支持存储null值。
然后我们在看ThreadLocal所包含的方法:
发现和WeakReference的结构很像!然后我们继续看下源码:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
定位到ThreadLocalMap,在看下源码:
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
...下面代码省略
}}
看到没,原来是ThreadLocal内部使用弱引用WeakReference来存储当前线程对象,以ThreadLocal为Key,value为Object。另外值得注意的一点是,上面的Entry继承自WeakReference,这样的好处就是能够及时回收Looper中创建的ThreadLocal对象。
接下来是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;
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 {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//...中间省略代码
msg.recycleUnchecked();
}
}
首先第一行,Looper.myLooper():
public static Looper myLooper() {
return sThreadLocal.get();
}
也就是从sThreadLocal静态对象中取出在调用Looper.prepare函数时存放的Looper对象。取出之后,调用Looper中的已经初始化好的对象MessageQueue取出消息池中的消息Message进行操作,取出之后就要将消息进行分发了,此时调用Message.target.dispatchMessage(msg),追溯到源码:
public final class Message implements Parcelable {
Handler target;
}
原来Message内部持有一个Handler对象,那么这个对象是在什么时候进行赋值的呢,继续看Handler源码:
public class Handler {
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}}
msg.target = this
就是这句!
我们搞明白了Handler在什么时候传递给Message之后,我们就明白消息是怎么回调的了,下面看dispatchMessage(Message msg)
函数的实现:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
这里有if else判断是如果回调函数不为空,则处理回调函数,否则处理消息,很简单,这里其实再次强调了Handler两种主要用法,sendMessage & post Runnable接口。
Message & MessageQueue
由于MessageQueue实际就是操作的Message对象,所以把他们结合在一起来说。
刚才讲到Handler有sendMessage(message)功能,看下源码,原来是Handler把Message对象发送到MessageQueue中,下面看个流程图:
最终定位到了下面的代码:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
queue.enqueueMessage(msg, uptimeMillis),继续看MessageQueue里面的这个函数
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.");
}
//此处省略若干代码...
Message p = mMessages;
boolean needWake;
synchronized (this) {
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;
}
if (needWake) {
nativeWake(mPtr);
}
return true;
}
我们不需完全看懂完整的代码,我们只看核心的部分,首先for循环部分是在不停的取出消息,然后取出消息调用了nativeWake函数,查看源码后发现:
private native static void nativeWake(long ptr);
是本地的实现,我们也不需要care(当然如果你有兴趣可以去查看源码),只需要知道这个函数是唤醒消息队列再次处理消息即可,而整个的消息的循环是采用Sleep-Wakeup机制。也就是说当队列中没有消息的时候,并不会不停的去取消息,而是进行休眠,休眠的意思大家想想自己的电脑就明白了,当再次点击电源键时,能够立刻唤醒进行工作,此处消息队列的处理也是一样,当消息队列中有消息的时候调用nativeWake来唤醒Looper线程。
另外值得注意的是,Message内部是采用消息池的机制,这样在获取的消息的时候回优先从消息池中取出可用的消息对象,如果没有再进行初始化,这样的好处就避免了不必要的内存开销。另外,Message类也提供了上述代码静态工厂方法Message.obtain()。
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
所以大家在使用的时候最好使用Message.obtain()
来构造Message对象。
3. 异步消息处理机制
说到这里,可以用一张图来总结整个的流程:
经过以上的几个关键的类的介绍,原理也大概明白了,其中间过程发生了什么,在什么时候发生,大家可以在源码中仔细查看。最后总结一下:
- Android应用程序的消息处理机制由消息循环、消息发送和消息处理三个部分组成的;
- Android应用程序的消息处理内部是使用ThreadLocal来将一个Looper对象绑定到一个线程上;
- Android应用程序的主线程进入空闲等待状态时不处理消息,来了消息之后利用本地nativeWake函数来进行唤醒;
- Android应用程序的消息采用消息池机制来存取消息。