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
目录
相关文章
|
30天前
|
消息中间件 存储 Java
Android消息处理机制(Handler+Looper+Message+MessageQueue)
Android消息处理机制(Handler+Looper+Message+MessageQueue)
38 2
|
14天前
|
消息中间件 存储 Java
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
|
17天前
|
消息中间件 存储 Java
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
41 1
|
21天前
|
消息中间件 存储 Java
Android消息处理机制(Handler+Looper+Message+MessageQueue)
Android消息处理机制(Handler+Looper+Message+MessageQueue)
43 2
|
3月前
|
开发工具 uml git
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
本文分享了下载AOSP源码的方法,包括如何使用repo工具和处理常见的repo sync错误,以及配置Python环境以确保顺利同步特定版本的AOSP代码。
371 0
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
|
3月前
|
Java Android开发 芯片
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
本文介绍了如何将基于全志H713芯片的AOSP Android源码导入Android Studio以解决编译和编码问题,通过操作步骤的详细说明,展示了在Android Studio中利用代码提示和补全功能快速定位并修复编译错误的方法。
109 0
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
|
3月前
|
开发工具 Android开发 git
全志H713 Android 11 :给AOSP源码,新增一个Product
本文介绍了在全志H713 Android 11平台上新增名为myboard的产品的步骤,包括创建新的device目录、编辑配置文件、新增内核配置、记录差异列表以及编译kernel和Android系统的详细过程。
95 0
|
3月前
|
Ubuntu 开发工具 Android开发
Repo下载、编译AOSP源码:基于Ubuntu 21.04,android-12.1.0_r27
文章记录了作者在Ubuntu 21.04服务器上配置环境、下载并编译基于Android 12.1.0_r27版本的AOSP源码的过程,包括解决编译过程中遇到的问题和错误处理方法。
158 0
|
6天前
|
编解码 Java Android开发
通义灵码:在安卓开发中提升工作效率的真实应用案例
本文介绍了通义灵码在安卓开发中的应用。作为一名97年的聋人开发者,我在2024年Google Gemma竞赛中获得了冠军,拿下了很多项目竞赛奖励,通义灵码成为我的得力助手。文章详细展示了如何安装通义灵码插件,并通过多个实例说明其在适配国际语言、多种分辨率、业务逻辑开发和编程语言转换等方面的应用,显著提高了开发效率和准确性。
|
5天前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
18 5