android Handler消息处理源码剖析

简介: 1、什么是HandlerA Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue.

1、什么是Handler

A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it --from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.

Handler允许你发送和处理Message(消息)/Runnable对象到当前线程的MessageQueue(消息队列)中。每个Handler实体对象都和当前所处线程的一个消息队列进行关联。当你创建一个新的Handler,这个Handler就和当前所处的线程和消息队列绑定在一起,从这个时刻开始,该Handler发送消息和Runnable对象到消息队列,在从MessageQueue取出消息的那一刻就开始执行消息。

2、Handler可以做什么

Handler是Android的一种消息处理机制,这种机制主要是为了解决Android应用中多线程的问题:在Android中不允许Activity新启动的线程访问该Activity里的UI组件,这样会导致新启动的线程无法改变UI组件的属性值。但实际开发中,很多地方需要在工作线程中改变UI组件的属性值,比如下载网络图片、动画等等。

Handler有两个作用:

(1)在工作线程中发送消息

(2)在UI线程中取出和处理消息

3、Handler提供的API

Handler提供两种方式将消息发送的队列:post和sendMessage。

Post:Post允许把一个Runnable对象加入到MessageQueue中,方法有post、postAtTime、postDelayed。

SendMessage:sendMessage允许把一个包含消息数据的Message放入MessageQueue中。包含sendEmptyMessage、sendMessage、sendMessageAtTime、sendMessageDelayed。

4、支撑Handler机制的架构图

img_5b2ae3d5461b7c2e714e7db13881896e.png
架构图

可以看到支撑Handler消息处理机制需要至少Handler、Message、MessageQueue、Looper四个类。

5、Handler、MessageQueue、Looper、Message的类图关系

img_c2be9af85acf4232e07946f8ea70f35e.png
类图关系

Handler:消息的真正处理类,具有发送消息、处理消息、移除消息的功能。依赖于MessageQueue和Looper;mQueue是MessageQqeue消息队列,通过sendMessage或者post方法发送的Message就存放在这个队列里面。mLooper是Looper消息调度器,用于循环取出mQueue中的Message。

MessageQueue:mMessages存放了所有的Message,以链表的形式存储。

Looper:用于循环从MessageQueue中取得Message,然后把、执行Message对应target的dispatchMessage方法。依赖于MessageQueue;mThreadLocal用于存放当前线程对应的Looper;sMainLooper表示UI主线程的Looper;mQueue是所有消息的队列。

Message:代表一条消息,依赖于Handler进行消息处理,对应一个Handler类型的target,在加入队列之前就会进行target设置。

6、Message消息从发送到运行源码解析

img_b542d1514bb685eca8d3fc59156cd5bc.png
handler使用示例

通常情况下,我们通过创建一个Handler和一个Message,然后调用sendMessage来发送消息。流程图如下:

img_ab0d15d0529c0d979d227b1405edf398.png
发送Message序列图

在创建Handler的时候,会触发Handler如下构造函数,该构造函数首先调用Looper.myLooper()创建一个Looper对象。

img_7621e6c7e43e7da575f42f123caad416.png
new Handler()

Looper.myLooper()实现如下,myLoop方法取得当前线程保持的Looper对象。

img_af0cc94548c35ce7612b36973ab02d90.png
Looper.myLooper()

myLooper()如果返回空值,在创建Handler时就会直接crash。因此在创建Handler时需要先创建Looper对象,并设置到Looper的sThreadLocal,;sTheadLocal用来保存当前线程的Looper对象,那当前线程的Looper对象是什么时候设置的呢?跟踪代码发现Looper.prepare方法会创建一个Looper,Looper对象在创建时会关联一个mQueue对象,最后将Looper设置到ThreadLocal,从此每个线程就会有自己的Looper对象。

img_3ce5e7dc63e44c0324cc4da3b570889b.png
Looper.prepare()
img_7e145fb6c85b6f77d6685eb789900243.png
Looper构造函数

因此在创建Handler之前需要先调用Looper.prepare()为当前线程创建一个Looper对象。创建完Looper对象后,就将该Looper的MessageQueue对象赋值给Handler,这样sendMessage()发送的Message就直接交给Looper的MessageQueue对象。

那么问题来了,当一个Message放入MessageQueue中后,谁来运行它呢?又是什么时机来运行的呢?我们似乎没有看到从MessageQueue取Message并运行的流程。上面我们提到了Looper类是不停地循环取Message的帮助类;没错,Android内部通过Looper的loop方法来不停地遍历MessageQueue。代码如下:

img_9f1a8a1c2370c5c7ec4d8409b27eb09a.png
Looper.loop()

当调用了Looper.loop方法后,首先通过myLooper()方法取得当前线程的Looper对象,也就是sThreadLocal中保存的值;然后取得当前线程Looper对象的mQueue,最后通过无线循环在取出mQueue的Message;运行Message对应target(Handler)的dispatchMessage方法。

因此,要实现一个Handler的正确做法应该如下:

img_bfecad43a645cdd56be36aeb016df171.png
正确使用Handler的方法

在loop()调用后会运行每个message.target.dispatchMessage()方法,那target是在什么时候设置的呢?在调用Handler的enqueueMessage方法时就会将自身设置给Message对象。

img_49cb8b261c29db5e15f0c7474083307c.png
sendMessage最终调用的方法

这样当Looper.loop()取得Message后就会直接调用handler的dispatchMessage(),该函数其实就是调用handleMessage方法,也就是我们在创建Handler时需要自己实现的方法。

img_fe129c6b0c35bafb7ac36d289342d2c1.png
handler最终执行Message的方法

如果用户在创建Message的时候传入了Callback就调用Message的Callback,Callback是一个Rnnable接口。如果没有传入Callback,就查看当前Handler在创建时是否指定了Callback,Handler的Callback只是一个handleMessage接口;如果都没有直接调用handler的handMessage()方法,这就是我们每次创建Handler都需要自己实现handlerMessage()的原因。

上面提到了在创建Handler时需要先调用Looper.prepare(),但是我们在实际编写代码中,例如在Activity中创建Handler没有主动调用Looper.prepare(),却没有报"Can't create handler inside thread that has not called

Looper.prepare()",这是因为Activity属于UI主线程,在APK启动时系统已经帮我们建好Looper了,代码就在AvtivityThread中,ActivityThread的main函数是整个APK运行的入口如下所示:

img_d10d749073a8a362899f263afa5578b5.png
ActivityThread main
目录
相关文章
|
22天前
|
XML Java Android开发
Android实现自定义进度条(源码+解析)
Android实现自定义进度条(源码+解析)
51 1
|
22天前
|
Java Android开发
Android反编译查看源码
Android反编译查看源码
23 0
|
3月前
|
数据采集 小程序 数据可视化
智慧校园电子班牌管理系统源码 Java Android原生
家长通过家长小程序端随时了解孩子在校的情况,实时接收学生的出勤情况,学生到校、离校时间。随时了解学生在校的表现、学生成绩排名,及时与教师沟通,关注孩子的健康成长。
30 0
智慧校园电子班牌管理系统源码 Java Android原生
|
3月前
|
安全 Android开发 开发者
【Android开发小技巧】扔掉这坑人的 Handler
【Android开发小技巧】扔掉这坑人的 Handler
40 0
|
3月前
|
算法 Java 定位技术
分享104个益智休闲安卓游戏源码,总有一款适合你
分享104个益智休闲安卓游戏源码,总有一款适合你
152 1
|
1天前
|
Linux 开发工具 Android开发
Docker系列(1)安装Linux系统编译Android源码
Docker系列(1)安装Linux系统编译Android源码
3 0
|
2天前
|
JSON 编译器 开发工具
VS Code阅读Android源码
VS Code阅读Android源码
9 1
|
1月前
|
定位技术 API 数据库
基于Android的在线移动电子导航系统的研究与实现(论文+源码)_kaic
基于Android的在线移动电子导航系统的研究与实现(论文+源码)_kaic
|
1月前
|
搜索推荐 测试技术 定位技术
基于Android的自助导游系统的设计与实现(论文+源码)_kaic
基于Android的自助导游系统的设计与实现(论文+源码)_kaic
|
1月前
|
Java 关系型数据库 应用服务中间件
基于Android的人事管理系统设计与实现(论文+源码)_kaic
基于Android的人事管理系统设计与实现(论文+源码)_kaic