Android线程间通信 Handler使用详解
作者:Quincy_Ye 发布时间:2022-10-16 09:22:33
前言
Handler,可谓是面试题中的一个霸主了。在我《面试回忆录》中,几乎没有哪家公司,在面试的时候是不问这个问题的。简单一点,问问使用流程,内存泄漏等问题。复杂一点,纠其源码细节和底层 epoll 机制来盘你。所以其重要性,不言而喻了吧。
那么今天让我们来揭开 Handler 的神秘面纱。为了读者轻松易读不烧脑,本系列文章按照使用篇,源码篇,面试篇来分篇浅析。
01、定义
在了解如何使用前,我们先来看看什么是 Handler ?
A Handler allows you to send and process and Runnable objects associated with a thread's .
可以用 Handler 发送并且处理与线程有关的 Runnable 对象。
这听起来,可能还有点迷惑。那么讲一个常见的场景来引出 Handler 吧。你接到了一个需求:有一个文件列表,点击文件后,开始下载该文件,下载完成后,在列表中对应文件上加上已下载的标识。
下载是一个耗时的动作,为了不引起 ANR(Application not response),我们通常需要通过子线程去完成这任务。但通常情况下(有特殊情况),我们又不能在非主线程中去更新 UI,所以就需要进行线程间通信。而 Handler 便发挥其作用了——进行线程间通信。
02、使用
第一步、创建
在创建 Handler 的时候,需要有注意一下是在主线程还是子线程。
//主线程
private val mHandler: Handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
when (msg.what) {
1 -> {}
}
}
}
//子线程
Thread {
Looper.prepare()
val handler = object : Handler() {
override fun handleMessage(msg: Message) {
when (msg.what) {
1 -> {}
}
}
}
handler.sendEmptyMessage(1)
Looper.loop()
}.start()
在主线程创建我们无需调用 Looper.prepare()
和 Looper.loop()
;
在子线程需要手动调用者两个方法。至于为什么主要是主线的这两个方法,在应用入口 main()
中系统帮我们完成了,具体见后续原理篇。
第二步、发送消息
发送消息有两大类方式(此处忽略定时和延迟发送)
第一种是 post(Runnable)
// 未简化写法,为了更好理解
mHandler.post(object :Runnable{
override fun run() {
// TODO 具体业务逻辑
}
})
第二种是 sendMessage(Message)
val msg = Message()
msg.what = 1
msg.obj = "Quincy"
mHandler.sendMessage(msg)
但其实两者本质上是没有区别的。因为上面两种方式最后都调用了 sendMessageDelayed()
,只是源码上帮我们把 Runnable 包装成了一个 Message。具体看下一篇,源码篇。
第三步、处理消息
处理消息,并非简单地在 handleMessage()
中处理即可,这还是要分情况的。为了方便分析,我们先浅浅窥探一下处理消息的源码
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg); // 注释1
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return; //注释2
}
}
handleMessage(msg); //注释3
}
}
注意上面三点注释,它们之间的关系。如果 Message.callback
不为空,即通过 post()
发送消息的,则调用 handleCallback(Message)
private static void handleCallback(Message message) {
message.callback.run();
}
否则,如果 mCallback 不为空且拦截了消息,则不再调用 handleMessage()
,
private val handler = object : Handler(object : Callback {
override fun handleMessage(msg: Message): Boolean {
if (msg.what == 1) {
return true//不会调用注释3
}
return false//会调用注释3
}
}) {
override fun handleMessage(msg: Message) {
when (msg.what) {
1 -> {}
}
}
}
否则将回调。
private val mHandler: Handler = object : Handler() {
override fun handleMessage(msg: Message) {
when (msg.what) {
1 -> {}
}
}
}
03、结语
一个线程中有多个 Handler ,但只有 1 个 Looper 和 1个 MessageQueue。哪在分发消息的时候,怎么知道发个哪一个 Handler 呢?
Looper 中有多少个
for( ; ; )
,分别的作用是什么呢?消息被处理后,Message 是被怎么处理的呢?
Handler 为什么会引起内存泄漏?
Handler 中有 Looper 的死循环,为什么没有卡死呢?(我认为就是要卡“死”,正是因为它我们的程序才没有退出)
来源:https://juejin.cn/post/7150287876276617253


猜你喜欢
- 前言在讲述线程池的前提 先补充一下连接池的定义连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用可以看到其连接
- 简单说明一下:线程池可以看做容纳线程的容器;一个应用程序最多只能有一个线程池;ThreadPool静态类通过QueueUserWorkIte
- 一、DES加密和解密package com.itjh.javaUtil;import java.io.UnsupportedEncoding
- java对象拷贝详解及实例Java赋值是复制对象引用,如果我们想要得到一个对象的副本,使用赋值操作是无法达到目的的:@Testpublic
- Spring Cloud Gateway(以下简称 SCG)做为网关服务,是其他各服务对外中转站,通过 SCG 进行请求转发。在请求到达真正
- 线程的状态New表示线程已创建,没启动的状态此时已经做了一些准备工作,还没有执行run方法中代码Runnable调用start方法之后的状态
- 在有些情况下,有很多列表不能一次性显示完整,需要对其进行分页处理博主自己也写了一个分页系统,在这里记录下来,方便以后直接拿来使用这篇文章De
- 1.使用的注意事项本节给大家带来基础UI控件部分的最后一个控件:DrawerLayout,官方给我们提供的一个侧滑菜单控件,和上一节的Vie
- 本文实例为大家分享了java实现航空用户管理系统的具体代码,供大家参考,具体内容如下题目内容:某航空公司在其航班到达的不同的国家的不同地方设
- 在Spring Boot集成Mybatis的项目中,如果出现SQL语句执行问题,我们需要进行排查。此时就需要打印对应的SQL语句,那么该如何
- MyBatis插入Insert、InsertSelective的区别逆向自动生成的mybatis对应配置Mapper文件里面,有两个方法,分
- 什么是 Spring Boot 插件?Spring Boot 插件是一种扩展机制,它提供了一种简单的方式来扩展 Spring Boot 的功
- 添加maven依赖<?xml version="1.0" encoding="UTF-8"?&
- 本文实例为大家分享了Java实现银行ATM系统的具体代码,供大家参考,具体内容如下一、前言银行ATM系列简单操作二、使用步骤1.创建用户信息
- 主线程和子线程的区别每个线程都有一个唯一标示符,来区分线程中的主次关系的说法。 线程唯一标示符:Thread.CurrentThread.M
- File类File类事java.io包中唯一代表磁盘文件本身的对象。File类定义了一些与平台无关的方法来操作文件,可以通过调用File类中
- 作为一位开发人员,都要有严格的代码规范。为此我总结了一些代码规范案例。目 录1. 前言2. 试用范围3. JAVA命名规范--3.1 公共约
- 在项目中选择器的使用是非常多的,以下是本人在项目中的一些常用的背景选择器的写法带边框下划线背景选择器效果图:上面布局中放了10个CheckB
- 本文实例讲述了Android实现的秒表计时器。分享给大家供大家参考,具体如下:package com.liu.time;import jav
- 获取接口调用凭据①接口说明access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token。开发者需要进行