学习 Android Handler 消息机制需要注意这些问题!(上)
一、提出问题
面试时常被问到的问题:
- 简述 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. 24000 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.geek-share.com/detail/2762691991.html
领取资料:【Android技术开发交流②】:979045005 点击链接加入群聊:https://jq.qq.com/?_wv=1027&k=5ev9C61
- 学习 Android Handler 消息机制需要注意这些问题!(下)
- Android学习历程10-Handler消息传递机制
- Android开发学习之路-Handler消息派发机制源码分析
- Android(java)学习笔记149:AsyncTask(异步任务)和Handler(消息机制)
- android handler发送消息需要注意的地方
- android学习13#--Handler消息传递机制
- Android学习心得(24) --- Android Handler消息机制源码分析
- Android应用开发学习笔记之多线程与Handler消息处理机制
- Android学习札记26:深入理解Android中的消息处理机制——Thread、Looper、MessageQueue和Handler(1)
- android异步消息处理机制 handler MessageQueue Looper 类 学习
- android学习14#--Handler消息传递机制实例应用
- Android学习之Handler消息传递机制
- 学习总结——android的消息机制handler
- Android(java)学习笔记202:Handler消息机制的原理和实现
- Android的消息机制学习(一)Looper,Handler,MessageQueue
- Android学习札记28:深入理解Android中的消息处理机制——Thread、Looper、MessageQueue和Handler(2)
- android学习之——Handler消息传递机制
- Android(java)学习笔记203:网页源码查看器(Handler消息机制)
- android消息处理机制学习(二)-Handler,Message,MessageQueue,Looper图例讲解
- android消息处理机制学习(三)-Handler,Message,MessageQueue,Looper源码分析