
Android 中的进程间通信,采用 Binder 机制,那线程间通信呢?Android 给出了一个机制 —— Handler。
一提起 Handler,就不得不提到它的 全家:
- Message
- MessageQueue
- Looper
- Handler
我们来一个个地解释一下。
Message —— 信件
Message 在何时都会代表『消息』的意思。在 Android 中消息还会附带一些其他的数据,供 暂存消息的 MessageQueue 和 分发消息的 Looper 以及 处理消息的 Handler使用。
Message 中包含可供 Handler 使用的三个字段,分别是 两个 int
字段 和一个 Object
字段。
我们来看看 Message 的定义:
|
从上面的代码,我们可以看出几个 Message 的特性:
- 有一个『回收池』的概念,如果 Message 完成使命之后被抛弃,那这个 Message 实例会被清空数据并扔到回收池中。如果再需要新建 Message 实例的时候,就直接从池中拿,避免了新建 Message 实例。
- 如果要新建 Message 实例,使用
Message.obtain()
方法,而不是调用new Message()
。 - Message 本身包含了一个 Handler 的引用,存储在 target 成员变量中,在发送消息时,会调用
target.sendMessage(this)
将自身传递到 Handler 中。
MessageQueue —— 邮筒
在 MessageQueue 的类注释里写着:
Low-level class holding the list of messages to be dispatched by a Looper. Messages are not added directly to a MessageQueue, but rather through Handler objects associated with the Looper.
它是一个低等级的类,拿着一堆 Message 等待 Looper 来分发。Message 并不是直接就添加到 MessageQueue 中,而是通过与 Looper 关联的 Handler 来操作的。
MessageQueue 最重要的功能当然就是维护本身的队列,提供进、出的方法给 Looper,以及掌管 Message 的生命周期。
我们分别来看看这几种特性是如何完成的。
|
读完上面的代码,我们可以发现,其实 MessageQueue 并不想我们想象的,有一个真实的『队列』存在,而是用 Message 的 next,建立了一个链表,形成了所谓的『队列』。在『进』的流程里比较简单,就是建立链表的过程;而『出』的流程相对复杂一些,是利用了一个死循环,因为每条 Message 都有自己『要被发送』的时间戳,如果时间没到,那还要等待一下,如果时间已到,就取出,并把其它的 Message 向队头提一位。
可以用两个流程图来展示『进』与『出』:
Looper —— 马车
Looper 是一个死循环。它负责不停地读取 MessageQueue 中的消息,如果一旦有消息,就拿出来,扔给 Handler,然后又进入自己的小圈子里不问世事。一旦 MessageQuee 里没有消息了,那它也就结束自己的工作。
我们看看它的代码:
*/ public final class Looper { ... static final ThreadLocal private static Looper sMainLooper; // guarded by Looper.class private static Observer sObserver; final MessageQueue mQueue; final Thread mThread; /** 将当前线程初始化为一个 Looper。 * 之后,在真正启动这个 Looper 之前,你就可以用这个 Looper 去创建 Handler 了。 * 在调用完 prepare() 之后一定要调用 loop() 方法,如果要停止,则调用 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)); } /** * 将当前线程初始化为一个 Looper,并将其当做应用的 main looper。 * 介个方法是由 Android 来调用的,所以理论上来说,你应该压根用不到这个方法。 */ public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } } /** * 返回应用的 main looper,在应用的主线程里存活的那个 */ public static Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; } } ... /** * 开始监听 message queue,一定记得要调用 quit() 来停止 Looper */ public static void loop() { // 拿到调用此方法的当前线程下的 Looper 实例 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(); // 可能会 block,因为在 MessageQueue.next() 中, // 调用了 nativePollOnce 方法,可能会引起阻塞。 // 也就是说,没有消息的时候,Looper 就在这儿『等着』。 if (msg == null) { // 木有 message,意味着 message queue 正在 quit // 一旦 return,这个 Looper 就结束了 return; } // 各种检查 ... try { // 发送消息 msg.target.dispatchMessage(msg); ... } catch (Exception exception) { ... } finally { ... } ... } } /** * 从 sThreadLocal 中获取当前线程下的 Looper * 此处利用了 ThreadLocal,某个线程下存储的数据,只有这个线程能读取,其他线程不可以,从而达到 * 线程与 Looper 的『绑定』效果 */ public static Looper myLooper() { return sThreadLocal.get(); } /** * 返回当前线程下 Looper 的 MessageQueue。 * 该方法必须从一个正在跑 Looper 的线程下调用,否则就会抛出 NullPointerException。 */ public static MessageQueue myQueue() { return myLooper().mQueue; } // 初始化 private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } ... // 返回当前线程是否是 Looper 的线程 public boolean isCurrentThread() { return Thread.currentThread() == mThread; } ... public interface Observer { // Message 分发之前被调用 Object messageDispatchStarting(); // Message 被 Handler 处理过了之后调用 void messageDispatched(Object token, Message msg); // Message 处理过程中出现异常时调用 void dispatchingThrewException(Object token, Message msg, Exception exception); } |
Looper 类比较清晰,职责也相对单一,就是用死循环的方式,一直通过 MessageQueue.next()
方法获取 Message,然后调用 msg.target.dispatchMessage(msg)
方法,将 Message 交给 Handler 处理(msg.target
是 Handler 类型)。
等任务全部处理完成后,使用 MessageQueue.quit()
清空消息,结束 Looper 的循环。
有一个比较经典的但是又很无厘头的问题:主线程的 Looper 死循环为什么不会导致 ANR?
这个问题其实算是偷换概念。
首先,死循环不是造成 ANR 的必然原因,ANR 是因为消息队列中的消息没有得到及时处理才造成的,比如 BroadcastReceiver 的 onReceive()
方法中处理事务超过了 10 秒,比如 onTouch()
事件超过了 5 秒,才会导致 ANR。
第二,主线程的 Looper 死循环,最多也就会导致个 OOM,但是主线程在没有消息时也会休眠、进入阻塞状态,当有新消息来临时,再被唤醒,分发消息,实际上对于内存的消耗非常小。
第三,如果主线程的 Looper 不循环了,那 main()
方法就抛出一个异常结束了,就整个应用就退出了。
另外,我们看到,prepare()
方法中使用到了 ThreadLocal 的机制,该机制的描述是:A 线程下使用了 ThreadLocal.set()
方法存储了某个资源,那么只有在 A 线程下才能通过 ThreadLocal.get()
方法拿到这个资源,从而实现了资源隔离 。对 ThreadLocal 的详解,在 这篇文章 里。
Handler —— 邮递员
Handler 顾名思义就是处理者。当接到 Message 的时候,就马不停蹄地开始工作。它的头脑很简单,给我活我就干,没活我就等着。
我们在初始化 Handler 的时候,有几种方式:
- 直接调用
new Handler()
。
我们来看看在这种情况下 Handler 的构造函数。
|
- 创建 Handler 实例时传入 Looper。
同样地,也看看这种情况下,会调用哪个构造函数:
|
有了 Looper 就可以直接赋值了,Handler 也并不关心当前的 Looper 是在哪个线程下,干就完了。
我们也观察到,Handler 的几个重载的构造函数里,总会有 Callback 参数,言下之意,是 Handler 在某个节点,应该会调用这个 Callback。我们来看看代码是不是这么写的,从 Looper 向 Handler 发送消息开始:
|
是了,如果在 Message 中有 Callback,那就直接回调 Message 中的 Callback,下面的就不走了。
如果给 Handler 设置了 Callback,那就调用,如果该 Callback 的实现类里,返回了true
,那就不再调用 handleMessage 方法;如果返回了false
,那就再调用一次 handleMessage 方法。
Callback 的定义是这样的:
|
刚才在 Looper 的代码中看到,msg.target.diapatch()
是在向 Handler 发送消息,那 msg.target
是什么时候被赋值的呢?
我们来看 Handler 中最著名的 post()
方法。
|
也就是说,Handler 不光只是处理消息,也要负责将消息添加到 MessageQueue 中。
关于这四大天王的关系,可以用一个关系图来展示一下: