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 ...
随机推荐
- Django:数据库驱动安装
import pymysql pymysql.install_as_MySQLdb() 常见MySQL驱动介绍: MySQL-python:也就是MySQLdb.是对C语言操作MySQL数据库的一个简 ...
- 用React仿钉钉审批流
引言 这几天帮朋友忙,用了一周时间,高仿了一个钉钉审批流.这个东西会有不少朋友有类似需求,就分享出来,希望能有所帮助.为了方便朋友的使用,设计制作的时候,尽量做到节点配置可定制,减少集成成本.如果您的 ...
- 混合开发模式是否可以在App备案制度下突围
网站 ICP 备案已施行了很久,我们也非常清楚必须在进行 ICP 备案后,网站才能在大陆范围合法运营,并且用户可以通过域名正常访问网站. 但是月初出了新规,明年起,国内的 App 也要像网站一样进行备 ...
- doris单机安装部署
原文出处 doris单机安装部署 下载Doris 环境要求 Linux系统:Centos7.x或Ubantu16.04及以上版本 Java运行环境: JDK8 java -version 在windo ...
- 文心一言(ERNIE Bot)初体验
引言 几个月前向百度提交了文心一言的体验申请,这两天收到了可以体验的通知,立马体验了一把.总体来说,文心一言基本上能做到有问必答,但是一些奇葩的问题还是会难住这位初出茅庐的 AI. 分享体验 我先后问 ...
- 《Web安全基础》02. 信息收集
@ 目录 1:CDN 绕过 1.1:判断是否有 CDN 服务 1.2:常见绕过方法 1.3:相关资源 2:网站架构 3:WAF 4:APP 及其他资产 5:资产监控 本系列侧重方法论,各工具只是实现目 ...
- CodeForces-1324D-Pair-of-Topics
题意 对于两个长度为\(n\)的数组\(a[]\)和\(b[]\),找到有多少对\(i\)和\(j\)\((i<j)\),满足\(a_i+a_j>b_i+b_j\) 分析 首先发现如果\( ...
- 搭建Minio分布式服务
本文主要介绍Minio的分布式环境搭建,安装比较简单,因博主只有一台window,所以使用VM虚拟机搭建的. 搭建前可以先了解下minio: 1.官方文档:https://docs.min.io/cn ...
- 提取protobuf定义文件结构
先安装protobuf的js支持包 npm install protobufjs test.proto文件如下所示 syntax = "proto3"; package Test; ...
- Prometheus + Grafana 搭建监控系统
前言 本文主要记录下如何使用 Prometheus + Grafana 搭建对各种服务的性能监控,涵盖对 Prometheus.Grafana 的基本介绍,以及如何使用二者进行对 Linux.MySQ ...