一, 提出问题
面试时常被问到的问题:
简述 Android 消息机制
Android 中 Handler,Looper,MessageQueue,Message 有什么关系?
这俩问题其实是一个问题, 其实只要搞清楚了 Handler,Looper,MessageQueue,Message 的作用和联系, 就理解了 Android 的 Handler 消息机制. 那么再具体一点:
为什么在主线程可以直接使用 Handler?
Looper 对象是如何绑定 MessageQueue 的?
MessageQueue 里的消息从哪里来? Handler 是如何往 MessageQueue 中插入消息的?
Message 是如何绑定 Handler 的?
Handler 如何绑定 MessageQueue?
关于 handler, 在任何地方 new handler 都是什么线程下?
Looper 循环拿到消息后怎么处理?
二, 解决问题
那么, 我们从主线程的消息机制开始分析:
2.1 主线程 Looper 的创建和循环
Android 应用程序的入口是 main 函数, 主线程 Looper 的创建也是在这里完成的.
ActivityThread --> main() 函数
- public static void main(){
- // step1: 创建主线程 Looper 对象
- Looper.prepareMainLooper();
- ActivityThread thread = new ActivityThread();
- // 绑定应用进程, 布尔标记是否为系统进程
- thread.attach(false);
- // 实例化主线程 Handler
- if(sMainThreadHandler == null){
- sMainThreadHandler = thread.getHandler();
- }
- // step2: 开始循环
- Loop.loop();
- throw new RuntimeException("Main thread loop unexpectedly exited");
- }
Looper.prepareMainLooper() 用来创建主线程的 Looper 对象, 接下来先看这个方法的实现.
2.1.1 创建主线程 Looper
- Looper --> prepareMainLooper()
- private static Looper sMainLooper; // guarded by Looper.class
- public static void prepareMainLooper(){
- // step1: 调用本类 prepare 方法
- prepare(false);
- // 线程同步, 如果变量 sMainLooper 不为空抛出主线程 Looper 已经创建
- synchronized (Looper.class) {
- if (sMainLooper != null) {
- throw new IllegalStateException("The main Looper has already been prepared.");
- }
- // step2: 调用本类 myLooper 方法
- sMainLooper = myLooper();
- }
- }
prepareMainLooper() 方法主要是使用 prepare(false) 创建当前线程的 Looper 对象, 再使用 myLooper() 方法来获取当前线程的 Looper 对象.
- step1: Looper --> prepare()
- // ThreadLocal 为每个线程保存单独的变量
- static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
- // Looper 类的 MessageQueue 变量
- final MessageQueue mQueue;
- // quitAllowed 是否允许退出, 这里是主线程的 Looper 不可退出
- private static void prepare(boolean quitAllowed) {
- // 首先判定 Looper 是否存在
- if(sThreadLocal.get() != null){
- throw new RuntimeException("Only one Looper may be created per thread");
- }
- // 保存线程的副本变量
- sThreadLoacal.set(new Looper(quitAllowed));
- }
- private Looper(boolean quitAllowed) {
- mQueue = new MessageQueue(quitAllowed);
- mThread = Thread.currentThread();
- }
prepare() 方法中用 ThreadLocal 来保存主线程的 Looper 对象. ThreadLocal 可以看作是一个用来储存数据的类, 类似 HashMap,ArrayList 等集合类, 它存放着属于当前线程的变量.
ThreadLocal 提供了 get/set 方法分别用来获取和保存变量.
比如在主线程通过 prepare() 方法来创建 Looper 对象, 并使用 sThreadLoacal.set(new Looper(quitAllowed)) 来保存主线程的 Looper 对象, 那么在主线程调用 myLooper()(实际调用了 sThreadLocal.get() 方法) 就是通过 ThreadLocal 来获取主线程的 Looper 对象. 如果在子线程调用这些方法就是通过 ThreadLocal 保存和获取属于子线程的 Looper 对象.
更多关于 ThreadLocal 的原理:
深入剖析 ThreadLocal 实现原理以及内存泄漏问题
问题 1: 为什么在主线程可以直接使用 Handler?
因为主线程已经创建了 Looper 对象并开启了消息循环, 通过上文的代码就可以看出来.
问题 2:Looper 对象是如何绑定 MessageQueue 的? 或者说 Looper 对象创建 MessageQueue 过程.
很简单, Looper 有个一成员变量 mQueue, 它就是 Looper 对象默认保存的 MessageQueue. 上面代码中 Looper 有一个构造器, 新建 Looper 对象时会直接创建 MessageQueue 并赋值给 mQueue.
问题 2 解决: 在 new Looper 时就创建了 MessageQueue 对象并赋值给 Looper 的成员变量 mQueue.
- step2: Looper --> myLooper()
- // 也就是使用本类的 ThreadLocal 对象获取之前创建保存的 Looper 对象
- public static @Nullable Looper myLooper() {
- return sThreadLocal.get();
- }
这个方法就是通过 sThreadLocal 变量获取当前线程的 Looper 对象, 比较常用的一个方法. 上文主线程 Looper 对象创建后使用该方法获取了 Looper 对象.
2.1.2 开始循环处理消息
回到最开始的 main() 函数, 在创建了 Looper 对象以后就调用了 Looper.loop() 来循环处理消息, 贴一下大致代码:
- public static void main(){
- // step1: 创建主线程 Looper 对象
- Looper.prepareMainLooper();
- ...
- // step2: 开始循环
- Loop.loop();
- }
- Looper --> loop()
- public static void loop() {
- // step1: 获取当前线程的 Looper 对象
- final Looper me = myLooper();
- if (me == null) {
- throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
- }
- // step2: 获取 Looper 保存的 MessageQueue 对象
- final MessageQueue queue = me.mQueue;
- ...
- // step3: 循环读取消息, 如果有则调用消息对象中储存的 handler 进行发送
- for (;;) {
- Message msg = queue.next(); // might block
- if (msg == null) {
- // No message indicates that the message queue is quitting.
- return;
- }
- ...
- try {
- // step4: 使用 Message 对象保存的 handler 对象处理消息
- msg.target.dispatchMessage(msg);
- end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
- } finally {
- if (traceTag != 0) {
- Trace.traceEnd(traceTag);
- }
- }
- ...
- msg.recycleUnchecked();
- }
- }
step1 : myLooper() 方法就是通过 ThreadLocal 获取当前线程的 Looper 对象, 注意在哪个线程使用该方法就获取的该线程的 Looper 对象.
step2 :me.mQueue, 这个 mQueue 就是上面问题 2 所说的在 Looper 对象创建时新建的 MessageQueue 变量.
step3 : 接下来是一个 for 循环, 首先通过 queue.next() 来提取下一条消息, 具体是怎么提取的可以参考下面文章的 4.2 节:
Android 消息机制 1-Handler(Java 层)
获取到下一条消息, 如果 MessageQueue 中没有消息, 就会进行阻塞. 那么如果存在消息, 它又是怎么放入 MessageQueue 的呢? 或者说 MessageQueue 里的消息从哪里来? Handler 是如何往 MessageQueue 中插入消息的? 先不说这个, 把这个问题叫作问题 3 后面分析.
-step4 :msg.target.dispatchMessage(msg); 这个方法最终会调用 Handler 的 handleMessage(msg) 方法. 同时这里又产生个问题: msg.target 是何时被赋值的?****, 也就是说 Message 是如何绑定 Handler 的? 先称之为问题 4. 那么接着看 Handler 的 dispatchMessage 方法:
- Handler --> dispatchMessage
- public void dispatchMessage(Message msg) {
- if (msg.callback != null) {
- handleCallback(msg);
- } else {
- if (mCallback != null) {
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- handleMessage(msg);
- }
- }
- private static void handleCallback(Message message) {
- message.callback.run();
- }
- public void handleMessage(Message msg) {
- }
可以看到该方法最后执行了 handleMessage() 方法, 这是一个空方法也就是需要我们覆写并实现的. 另外 dispatchMessage() 也体现出一个问题:
消息分发的优先级:
Message 的回调方法: message.callback.run(); 优先级最高;
Handler 的回调方法: mCallback.handleMessage(msg) 优先级次于上方;
Handler 的回调方法: handleMessage() 优先级最低.
到这里 Looper 循环并通过 Handler 发送消息有一个整体的流程了, 接下来分析 Handler 在消息机制中的主要作用以及和 Looper,Message 的关系.
后续见学习 Android Handler 消息机制需要注意这些问题!(下)https://www.jianshu.com/writer#/notebooks/34739187/notes/42386977/preview
来源: http://www.jianshu.com/p/aab05cd13100