前言
Android消息机制,是Android中核心机制之一,进阶路上的基础知识,其主要指的是Handler运行机制,而Handler运行需要底层的MessageQueue、Looper的支撑,下面我们共同探索。
核心概念综述
Handler
对于Handler,许多初学者认为它基本用于更新UI。
而官方解析道:
There are two main uses for a Handler:
(1) to schedule messages and runnables to be executed as some point in the future;
(2) to enqueue an action to be performed on a different thread than your own.
翻译过来:
(1) 调度message、runnable的执行
(2) 使动作在不同的线程中执行。
其并没有提及更新UI的作用,从本质上看,Handler并不是专门用来更新UI的,只是对于开发者来说,它常被用于更新UI,实际Handler是Android消息机制的一个重要组成部分,更新UI不过是消息传递与处理的其中一部分。
Message
代表一个行为或是一串动作,每一个消息加入消息队列时,都有明确的Handler作为目标。
MessageQueue
字面意思 - 消息队列,以队列形式对外提供插入、删除工作,但内部以单链表存储结构实现,并不是真正的队列,而是采用单链表的数据结构来存储消息列表。
Looper
消息循环,Looper会以无限循环形式去MessageQueue查找是否有新消息,如果有则处理,没有则等待。
注意:
- 线程中默认没有Looper,如果使用Handler就必须为线程创建Looper。创建Handler之前,需要Looper.prepare(),最后要补上Looper.loop();
- 主线程(UI线程),ActivityThread创建时已经初始化Looper了,故可以直接使用
ThreadLocal
Handler创建时候需要获取到当前线程中的Looper来构造消息循环系统,Handler内部正是通过ThreadLocal来获取的。
ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。
详解可参见:解密ThreadLocal
消息机制流程解析
总体流程
创建 Handler时,会获取当前线程的Looper来构建消息循环系统,如果当前线程没有Looper,则会报错。
Handler创建完毕后,内部的Looper以及MessageQueue就可以与Handler一起协同工作了,Handler通过post投递Runnable或者send方法发送消息(post方法最终也是通过send方法来完成的),Runnable与Message最终都会交给Looper中处理。
Send方法流程:通过MessageQueue的enqueueMessage方法将消息放入消息队列,Looper发现有新消息到来,则处理该消息。最终消息中的Runnable或者HandlerMessage方法会被调用。
注意:
- Looper运行在Handler所在线程中的,当消息交给Looper处理时候,业务逻辑已经成功切换到创建Handler所在的线程中了。
- 每个线程都至多只可以有一个Looper对象,也只可以有一个与该Looper对象关联的MessageQueue对象。
- 每个线程可以有多个Handler对象,Looper将Message取出来后,会调用与该Message关联的Handler的dispatchMessage方法。
下面引用一张关系来阐述:
解析:
- Runnable和Message可以被压入某个MessageQueue。而需要注意的是,某种类型的MessageQueue一般只允许保存相同类型的Object
- 实际上,源码中会对Runnable进行相应处理,转换成Message再放入到对应MessageQueue中
流程总结:
- Handler创建时,通过sThreadLocal取出当前对象的Looper构建消息循环系统,若当前线程没有Looper则报错
- Handler通过post或send方法将Runnable投递或者Message发送,Message入队到MessageQueue,同时Looper死循环查看MessageQueue是否有消息,不断取出新消息,交给对应Handler处理。(取出消息的时,内部实现了跨线程通信)
- 即:Looper不断获取MessageQueue中的一个Message,然后由Handler来处理
深入理解 - 源码探究
消息机制相关类联系
- Thread - Looper 1对1,一个Thread最多只能拥有一个Looper
- Looper - MessageQueue 1对1,一个Looper只能对应一个MessageQueue
- MessageQueue - Message 1对N,一个MessageQueue中有N个Message
- Message - Handler N对1,每个Message最多指定一个Handler
- Thread - Handler 1对N,由上述关系推断
相关类的类关系图:
Handler
源码路径:frameworks/base/core/java/android/os/Handler.java
Handler功能一 - 处理Message(本职工作)
- Looper从MessageQueue中取出一个Message后,首先会调用dispatchMessage方法进行消息派发
- dispatchMessage方法会根据具体策略来将Message分发给相应负责类处理(上述源码中为默认处理方式)
- Handler的扩展子类可以重写上述两个方法来改变它的默认行为
Handler功能二 - 将Message压入MessageQueue
注意:该功能的设计形成了一个神奇的“循环圈”,熟悉类关系图(见上文),便很好理解了:
Handler -(压入消息)-> MessageQueue -(Looper取出消息)-> Message -(传递消息给Handler处理)-> Handler
MessageQueue
源码:frameworks/base/core/java/android/os/MessageQueue.java
Looper
源码路径:frameworks/base/core/java/android/os/Looper.java
Android源码角度上看,消息机制设计思想更贴近下图:
解析:从上文类关系图可知,Looper含有mQueue、mThread,Handler含有mQueue、mLooper,MessageQueue含有mMessages。这样,图就简化成Looper与Handler(其实还有Thread)之间的关系了。
Looper在普通线程中的工作过程:
解析:
上述代码关键有三个步骤:
- Looper.prepare(); - Looper的准备工作
- 创建处理消息的Handler
- Looper.loop(); - Looper的开始运作
步骤一 - Looper.prepare()
步骤二 - 创建处理消息的Handler
步骤三 - Looper.loop()
Looper总结:不断从消息队列中取出消息,再分发给对应的Handler,如果消息为空,则跳出死循环,进入睡眠以让出CPU资源。
拓展:具体事件处理过程中,程序会post新的事件进入队列,其他进程也可能投递新的事件到这个队列中,APK应用程序本质上就是不停地执行“处理队列事件”的工作,直至它退出。
ThreadLocal
ThreadLocal是线程内部的数据存储类,各个线程相互隔离、相互独立。
主要作用:
- 实现Looper在线程中的存取,使得线程与Looper能够一一对应
- 实现负责逻辑下对象的传递,例如:让监听器作为线程内的全局对象,通过ThreadLocal,线程内部只要通过get即可获取到,简单又强势(当然也可以有其他方法解决:1、以函数参数方式传递;2、以监听器作为静态变量供线程访问;两种方法都有其局限性,综合来看,采用ThreadLocal是最好解决方法)
源码分析:
学习资源推荐
Handler Looper MessageQueue 详解
Android消息处理机制(Handler、Looper、MessageQueue与Message)