1. 吸血鬼和狼人之战
在讨论 Handler 机制之前,我们先来讲一个故事:
吸血鬼和狼人打仗,战争进入僵持阶段,吸血鬼兵团为了打探狼人军团的动静,决定派遣一名密探潜入狼人军团内部刺探军情,同时命令一名吸血鬼听风者守候在家门口等待吸血鬼密探发来的情报。
吸血鬼密探带着专门传递情报的吸血鬼通讯员潜入了狼人军团的内部。吸血鬼密探很快和狼人军团内部打成一片,并且获得了很多有用的军情,密探需要把这些重要的消息发回给吸血鬼总部,好让吸血鬼军团随时做好下一步攻击的准备,同时为了保护自己不被暴露,他自己不能亲自传递消息,于是就指示随从的其貌不扬的吸血鬼通讯员把这些消息一一传回总部。
吸血鬼通讯员得到了命令,悄悄地将消息放入信鸽传回了吸血鬼情报中心。
情报中心的吸血鬼听风者时刻等待着家门口放置的信鸽笼子,一旦发现有新的信鸽飞回来就马上取回消息交给总部的吸血鬼长老。
吸血鬼长老按照消息传回的先后顺序和消息的内容分别做出了不同的军事准备。
很快,狼人军团的一切都被吸血鬼掌握了,但是狼人也不是吃素的,他们也和吸血鬼一样进行着各种各样的间谍活动,于是一场吸血鬼和狼人的生死大战马上就要登场了!
2. 重要角色关系
在上面我们讲述的故事当中,在吸血鬼阵营中有几个重要角色:长老、密探、听风者、通讯员,当然别忘了还有我们的信鸽和鸽笼,它们是消息的承载物。我们来图解一下它们角色关系:
在这张图中,我们描述了各个角色的职责和相互间的交互关系,同时我们还看到了每个角色和 Handler 机制中几个重要类的对应关系:
吸血鬼密探或者说吸血鬼密探和狼人军团之间的利用关系表示主线程以外的工作线程(往往如此,不过也可以在主线程中,当你需要延迟一段时间完成一项工作的时候)。
Handler 有两个主要的功能:构造消息并将消息发送到消息队列;处理由 Looper 从消息队列中派发来的消息。在上面的故事中我们将其分身为两个角色:吸血鬼通讯员(构造并发送消息);吸血鬼长老(处理消息)。
信鸽表示正在派发中的 Message,即消息或者说情报,它标识了识别消息的 ID、消息的内容(args, obj),或者直接就是一个 规定何时需要处理的任务流程(Runnable or Callback)。
鸽笼表示 MessageQueue,即消息队列。按照需要需要处理的时间先后顺序进行排队,若没有指定延迟时间,则默认按照消息构造的时刻进行排序。
吸血鬼听风者毫无疑问扮演着 Looper 的角色,它不停地盯着 MessageQueue,如果其中有符合处理条件的 Message(时间到,未超时),就从队列中摘下一个消息立即交给吸血鬼长老进行处理(handleMessage)。
3. 你需要了解的几个角色特性
- 一个线程中只有唯一的一个 Looper,该 Looper 通过 ThreadLocal 和所属线程绑定在一起,ThreadLocal 将 Looper 封闭在所属的线程中,不为其它外部线程所共享,所以你无需担心对 Looper 的访问会造成线程安全问题;
- MessageQueue 在 Looper 中创建,属于 Looper 的一个实例变量,所有对 MessageQueue 的引用均指向 Looper 中的这个实例,所以 MessageQueue 也是线程唯一的;
- 综上,对 Looper 和 MessagQueue 的访问都是线程安全的;
- 每一个 Handler 从开始创建便与一个 Looper 和 MessageQueue 相关联;
- Handler 派发消息(sendXXX(), postXXX())和处理消息(dispatchMessage(), handleXXX())的操作实际都由所引用的 Looper 和 MessageQueue 代为处理,所以它们都是线程安全的;
- Handler 机制的这种线程安全性让外部线程可以很安全地引用本线程的的 Handler 以方便线程之间的通信,比如数据库操作线程通过引用 UI 线程的 Handler 将操作结果传递给 UI 线程进行处理。
吸血鬼长老和通讯员可以有多个,而听风者需要迅速提取和传递消息,多个听风者协同处理反而增加麻烦,同样多个鸽笼会让信鸽无所适从,总之一切为了处理的迅速和便捷。另外,当吸血鬼密探委托吸血鬼通讯员传递消息开始的工作实际上全部属于吸血鬼兵团内部事务,从这里开始,相当于设置了一堵墙(ThreadLocal),除非吸血鬼兵团被击溃,否则内部事务是不会被外部所干扰的——线程安全性。
一个在新线程中典型的 Looper 和 Handler 交互实例:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare(); // 创建 Looper 并绑定到线程
mHandler = new Handler() { // 创建 Handler 并绑定到 Looper
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop(); // 启动 Looper,监控消息队列
}
}
4. 类图
我们再来看一下 ThreadLocal, Looper, MessageQueue, Message, Handler 之间的类关系:
从这个类图中,我们可以更加清晰地看到 Handler 机制中各个类之间的交互关系,Looper 和 ThreadLocal 的绑定保证了线程安全。这里我们剥离了 Handler 中的跨进程通信机制(IPC),留待以后介绍。
5. 总结
- Handler 机制是 android 中的一种线程通信机制,基于 Handler 的多个线程之间可以通过该机制快速直接地进行切换,并且这种切换是线程安全的;
- 在 android 中,UI 线程是基于 Handler 机制的,可以通过 Looper.getMainLooper() 获取 UI 线程的 looper,通过这样的方法,可以判断当前线程是否为 UI 线程,也可以将 main looper 作为参数构造 UI Handler,从任意工作线程中切换到 UI 线程;
- 在实际操作中,通常利用 Handler 机制完成 UI 线程和工作线程之间的通信,或者在当前线程中一个合适的时间点执行相关操作。
6. 参考