Android 的异步消息处理机制
前言
Android中的异步消息处理机制主要有四部分:Message、Handler、MessageQuene、Looper。这一消息处理机制也称为Handler机制。Handler机制是支撑整个Android系统运行的基础,本质上是因为Android系统是由事件驱动的,而处理事件的核心就在于Handler机制。
Messgae
Messgae是在线程间传递的信息,它可以携带少量的信息以便在线程之间传递。
message.what // 标识是哪条信息
message.arg1 // 存放整型数据
message.arg2 // 存放整型数据
message.obj // 存放任意对象
生成Message
Handler h = new Handler();
...
Message m1 = new Message();
Message m2 = Message.obtain(); //推荐使用,消息池
Message m3 = h.obtainMessage(); //推荐使用,消息池
使用消息池获取 Message 实例,并不一定直接创建新实例,而是先在消息池中先看是否存在可用实例,存在则直接取出并返回该实例。
Handler
Handler作为线程间消息的处理者,主要用于发送和处理消息。
构造方法
Handler()
Handler(Callback)
Handler(Looper)
Handler(Looper, Callback)
其中前两个不传入Looper的构造方法已被废弃(Android 11,R)。官方解释是在Handler的构造中隐式指定Looper可能会导致错误发生。Handler中引用的是哪个线程的Looper,就在哪个线程中处理消息。
使用方法
- 在主线程中创建一个Handler,然后在子线程中使用它
- 在子线程中创建一个运行在主线程中的Handler并使用它
- 新建一个静态内部类,将Activity作为弱引用放到Handler中使用(推荐使用)
常用函数
- sendMessage(Message):将消息对象发送到消息队列MessageQuene中,将自身(Handler)的引用传递给Message的目标处理器target,调用MessageQueue的enqueueMessage方法
- handleMessage(Message):根据消息的标识,进行消息的处理。继承Handler类新建Handler时,需要重写此方法。
MessageQueue
MessageQueue是一个消息队列,用于存放消息,其内部通过单链表的数据结构来维护消息列表。消息经过Handler发送至这里之后,等待被处理。MessageQueue由Looper对象进行管理,不需要手动创建。每个线程只有一个MessageQueue对象。
MessageQueue在Handler的构造函数中被赋值,赋值对象为Looper,Looper对象有一个字段是 MessageQueue。是可以通过Looper.myQueue()获取当前线程的 MessageQueue。
- enqueueMessage(Message, long)方法作用
- 获取队列头
- 如果消息不需要延时(long参数),或者消息(传入的Message)的执行时间比头部消息早,或者当前队列是空的,就插到队列头部
- 如果不是以上的情况,需要将当前消息插入到消息队列的中间位置,通过遍历整个队列,当队列中某个消息的延时比当前消息晚时,将当前消息插入到这个消息的前面。由此得知,消息队列是一个依据“执行时间先后”链接起来的单向链表。
Looper
Looper作为MessageQueue的管理者,是消息循环的核心,不断从其MessageQueue对象中获取消息。在Handler的前两个构造函数中,Handler通过Looper.myLooper方法获取到了Looper对象,但也有可能获取失败,所以后两个构造中的Looper参数就显得十分重要。线程和Looper一一对应,每个线程只有一个Looper。除了主线程有默认 Looper以外,其他线程默认没有 Looper 对象。要想让新创建的线程拥有Looper,应先调用 Looper.prepare()、再调用Looper.loop()。可以使用HandlerThread类创建新线程,该类创建的新线程(非主线程)也含有Looper对象。
主线程由ActivityThread类创建,ActivityThread类(中的main方法)也是整个App的入口,主线程Looper也是在此处创建的。主线程Looper可以通过Looper.getMainLooper()获取。当前线程Looper可以通过Looper.myLooper()获取。
Looper分发消息,是通过一个方法为Looper.loop() 的死循环执行的。在该死循环中,Looper不停地从其MessageQueue对象中获取消息。获取到消息后,根据其target(Handler)的dispatchMessage去分发消息。消息先分发给Message的Callback,未定义的情况下会在分发给Handler的CallBack(从Handler的构造中有所体现),此处的回调与直接声明Handler时相同的是,都要重写handleMessage()方法。
也就是说,主线程Looper获取到Message后,根据Message的target字段找到了发送消息的Handler,紧接着调用了Handler的handleMessage方法。在此处,不论Handler在那个线程被创建,主线程Looper只在主线程调用Handler的handleMessage方法,这样也就完成了线程的切换——“子线程返回主线程”。
ANR 与 Looper
在Linux操作系统上执行一些命令,一般来说程序运行完毕后终端返回信息然后程序都会直接退出。Android应用打开后,不会自己退出的原因就是因为程序还在运行,即使程序中“不写代码”。此处程序的运行,具体到代码中,就是Looper.loop()这个死循环方法,这个方法阻塞了主线程,使得程序一直运行不会退出。
整个Android系统中,驱动程序运行的大部分都是事件,这些事件都作为Message被发送到了MessageQueue中,由Looper进行分发,然后再进行处理。
ANR是Application Not Resopnding,也就是程序无响应,通常是由于在主线程做了耗时操作导致的,其本质不是阻塞了主线程,而是阻塞了主线程Looper的loop()方法,导致loop()无法从MessageQueue中获取消息,进而出现了ANR事件。
Handler 导致的内存泄漏问题
内存泄漏就是说该回收的对象没有回收。倘若直接在Activity中声明一个匿名Handler,Android Studio 就会这样提示:
This Handler class should be static or leaks might occur (anonymous android.os.Handler)
发生这件事的原因是,Java中非静态内部类会引用外部类对象。当Activity在1s内退出时,由于Handler会被Message持有,而Handler作为内部类的实例又会持有Activity,导致Activity退出时无法被回收,产生内存泄漏。
解决办法:
- 新建一个静态内部类继承Handler,将Activity作为弱引用放到Handler中使用
- 页面退出的时候,在
onDestroy中,调用Handler的removeMessages方法,将所有的消息 remove 掉,这样也能消除持有链
Android 的异步消息处理机制的更多相关文章
- Android多线程----异步消息处理机制之Handler详解
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...
- Android Handler 异步消息处理机制的妙用 创建强大的图片加载类(转)
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38476887 ,本文出自[张鸿洋的博客] 最近创建了一个群,方便大家交流,群号: ...
- Android Handler 异步消息处理机制的妙用 创建强大的图片载入类
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38476887 ,本文出自[张鸿洋的博客] 近期创建了一个群.方便大家交流,群号: ...
- android学习-异步消息处理机制
消息处理机制主要对象:Looper,Handler,Message(还有MessageQueue和Runnable) Looper不断从MessageQueue消息队列中取出一个Message,然后传 ...
- 【转】Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 很多人面试肯定都被问到过,请问Andr ...
- Android 异步消息处理机制 让你在深入了解 Looper、Handler、Message之间的关系
转载请注明出处:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 非常多人面试肯定都被问到过,请问And ...
- Android线程之异步消息处理机制(三)——AsyncTask
Android的异步消息处理机制能够很完美的解决了在子线程中进行UI操作的问题,但是为了更加方便我们在子线程中对UI进行操作,Android还提供了另一个很好用的工具,AsyncTask就是其中之一. ...
- Android学习之异步消息处理机制
•前言 我们在开发 APP 的过程中,经常需要更新 UI: 但是 Android 的 UI 线程是不安全的: 如果想更新 UI 线程,必须在进程的主线程中: 这里我们引用了异步消息处理机制来解决之一问 ...
- Android异步消息处理机制
安卓子线程无法直接更改UI,所以需要异步消息处理机制来解决 <?xml version="1.0" encoding="utf-8"?><Li ...
- Android 异步消息处理机制解析
Android 中的异步消息处理主要由四个部分组成,Message.Handler.MessageQueue.Looper.下面将会对这四个部分进行一下简要的介绍. 1. Message: Messa ...
随机推荐
- Angular报错:Error: Unknown argument: spec
解决方案 使用--skip-tests代替 效果展示 可以看到spec.ts消失了 参考链接 https://stackoverflow.com/questions/62228834/angular- ...
- QPushButton中常用的方法
常用方法如下所示: setCheckable():设置按钮是否已经被选中,如果设置为True,则表示按钮将保持已点击和释放状态. toggl():在按钮之间进行切换 setIcon():设置按钮上的图 ...
- pandas 利用openpyxl设置表格样式
writer = pd.ExcelWriter('/home/leo/Desktop/项目测试/922904.xlsx', engine='openpyxl') new_data.to_excel(w ...
- 从read 系统调用到 C10M 问题
一.前言 从上个世纪到现在,工程师们在优化服务器性能的过程中,提出了各种不同的io模型,比如非阻塞io,io复用,信号驱动式io,异步io.具体io模型在不同平台上的实现也不一样,比如io复用在bsd ...
- 处理css/js兼容性的工具之超重要的browserslist
这篇 webpack处理css资源 文章中使用到的工具 browserslist 对于兼容性处理来说非常重要!这一篇来仔细说说. 查询兼容性 不同浏览器对于 css / js 的属性可能存在兼容性,具 ...
- nginx搭建静态文件下载服务器
配置文件大致内容 server { # 监听8001端口 listen 8001; server_name 192.168.0.2; # 指定使用utf8的编码 charset utf-8; # 内容 ...
- 8、Mybatis之自定义映射
8.1.环境搭建 8.1.1.创建新module 创建名为mybatis_resultMap的新module,过程参考5.1节 8.1.2.创建t_emp和t_dept表 CREATE TABLE ` ...
- 十年磨一剑的华为云GES,高明在哪
本文分享自华为云社区<华为云GES:十年磨一剑,打造业界一流的云原生分布式图数据库>,作者:GES图引擎服务小图 . 1.浅谈云原生图数据库 图数据库(graph database)是一个 ...
- zbolg首页-文章内标签调用代码
{if count($lhantItem.Tags)>0}{foreach $lhantItem.Tags as $i => $tag} <a href='{$tag.Url}' t ...
- linux日常运维(三) GRUB 2的维护
GRUB 2简介 GRUB GRUB是linux系统默认的引导加载程序.linux加载一个系统前,它必须有一个引导加载程序中特定指令(比如MBR记录)去引导系统.这个程序一般是位于系统的主硬盘驱动器或 ...