带你深入了解Android Handler的用法
Android中,Handler是一类用于异步消息传递和线程之间通信的基础框架。一个Handler是一个线程的处理器,可以接收消息,并调度运行它们。使用Handler,应用程序可以将处理器与一个线程关联,以将来的时间运行任务。而使用Handler,就可以避免启动额外的线程,从而提高代码的效率。本文将详细介绍Handler的概念、使用方法和常见问题等。
什么是Handler?
在 Android 开发中,使用多线程是非常常见的,但是在 Android 中,有一个 UI 线程,也就是主线程,所有的UI操作必须在主线程中完成,否则就会抛出 CalledFromWrongThreadException 异常,这个异常的原因是 Android 不允许在子线程中直接访问 UI 控件。而 Handler 的主要作用就是用于子线程与主线程之间进行通讯,在应用程序内传递消息、更新 UI 等各种场景下,都可以使用 Handler 来实现。
一个Handler是与Looper关联的对象,它拥有处理消息队列的能力,即将消息加入到队列中,并在将来的某个时间处理这些消息。每个线程只能有一个Looper,而每个Looper所属的线程可以拥有一个或多个Handler。当创建一个新的Handler实例时,它们会自动附加到创建它的线程的消息队列。当线程被销毁时,它的消息队列也会被销毁。
Handler使用消息队列来进行异步任务的处理,消息队列是基于先进先出(FIFO)的原则,即在消息队列中消息加入的顺序与取出的顺序相同。消息通常是如下形式的数据结构:
class Message {int what; //消息idint arg1; //消息参数1int arg2; //消息参数2Object obj; //消息的Object对象Handler target; // 处理该消息的Handler对象//省略其他代码
}
为什么要使用Handler?
在 Android 应用程序中,不能在非 UI 线程中直接更新界面,因为 Android 不支持跨线程更新界面。也就是说我们必须找到一种间接的方法来实现,在应用程序中常用的解决方法有以下几种:
-
Timer、 TimerTask :使用定时器 Timer 和 TimerTask 进行轮询,实现 UI 刷新。
-
AsyncTask :非常适合于轻量级的后台任务,并发数默认为一个。
-
Thread :使用普通线程或者线程池配合 Handler、 Message, Runnable,Timer 等类来更新 UI。
实际上,这些方法之间并不是完全割裂的,各个方法之间经常会相互搭配使用。
- 在 Handler 的基础上,Android 还提供了 Looper 和 MessageQueue 来协同完成线程的消息传递工作。
如何使用Handler?
要实现在线程中更新 TextView 的文字,基本的实现思路如下:
-
在子线程中新建一个 Handler。
-
在子线程中开启轮询,执行子线程任务。
-
在子线程任务执行完毕后,新建一个 Message 对象,并通过 Handler 将 Message 发送出去。
-
在主线程中新建一个 Handler,并在其 handleMessage 方法中将 Message 对象取出。
-
通过 Message 中的内容更新 UI。
代码如下:
private TextView textView;
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);textView = (TextView)findViewById(R.id.textview);mHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {textView.setText((String)msg.obj);}};new Thread(new Runnable() {@Overridepublic void run() {// 子线程耗时操作Message msg = mHandler.obtainMessage();msg.obj = "子线程更新UI";mHandler.sendMessage(msg);}}).start();
}
Handler的常见使用方式
在 Android 应用程序中,将消息发送给 Handler 进行处理的方式有两种:
- post()方式
可以通过调用 Handler 的 post() 方法,将 Runnable 对象通过 MessageQueue 发送到消息队列中,即可让主线程处理相应的操作。这种方式可以用于解决在子线程中不能进行 UI 操作的问题,例如我们可以在子线程中通过 post 方式将更新 UI 的任务传递到主线程来完成,这样就不会因为在非 UI 线程中更新 UI 而导致 ANR(Application Not Responding)了。
示例代码如下:
new Thread(new Runnable() {@Overridepublic void run() {//子线程中更新UIHandler mainHandler = new Handler(Looper.getMainLooper());mainHandler.post(new Runnable() {@Overridepublic void run() {//在主线程中更新UI}});}
}).start();
- sendMessage()方式
前面提到了,sendMessage方法会将Message发送到消息队列的尾部并等待处理,而post方法则是将Runnable对象直接添加到消息队列中。sendMessage 和 post 的区别在于,sendMessage 会将传递的消息封装成 Message 对象,最后再发送到 MessageQueue 中,适用于需要传递参数并处理结果的情况。
常见的使用方式包括:
- Handler.sendMessage(Message msg) 发送 Message 对象到消息队列。
- Handler.sendMessageDelayed(Message msg, long delayMillis) 延迟指定时间后发送 Message 对象到消息队列。
- Handler.sendMessageAtTime(Message msg, long uptimeMillis) 在指定时间后(毫秒)将 Message 对象加入到消息队列中。
- Handler.sendEmptyMessage(int what) 发送一个不携带任何数据的空消息。
- Handler.post(Runnable r) 将 Runnable 对象添加到消息队列中。
示例代码如下:
private Handler mHandler = new Handler(Looper.getMainLooper()) {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case 1://处理消息break;case 2://处理消息break;default:break;}}
};private void sendMessage() {Message msg = mHandler.obtainMessage();msg.what = 1;msg.arg1 = 2;msg.obj = "message content";mHandler.sendMessage(msg);
}private void sendEmptyMessage() {mHandler.sendEmptyMessage(2);
}private void postRunnable() {mHandler.post(new Runnable() {@Overridepublic void run() {//执行操作}});
}
Handler的作用及其核心流程
Handler的常见用途包括:
- 在子线程中执行任务后,通过Handler回到主线程更新UI。
- 延迟执行任务或周期性执行任务。
- 将多个耗时任务按顺序串行执行。
使用Handler的核心流程如下:
- 首先,创建一个Handler实例,并与当前线程的Looper关联。
Handler mHandler = new Handler(Looper.getMainLooper());
- Handler负责处理消息队列。要想从消息队列中获取消息,需要使用Handler的公共方法之一post()或sendMessage()。通常,它们的使用方式相似。
mHandler.post(new Runnable() {@Overridepublic void run() {// UI更新代码}
});
或者:
Message message = Message.obtain();
message.what = MSG_ID;
message.arg1 = arg1;
message.arg2 = arg2;
mHandler.sendMessage(message);
- 当消息被加入到消息队列后,Handler会根据先进先出(FIFO)的原则将它们按顺序从队列中取出,然后调用associated Handler对象的handleMessage()方法处理它们。定制Handler子类的常见做法就是在handleMessage()方法内填充相应的处理逻辑。
@Override
public void handleMessage(Message msg) {switch (msg.what) {case MSG_ID:// 执行任务逻辑break;default:super.handleMessage(msg);break;}
}
Handler的基本用法
- 在主线程中使用Handler
在主线程中使用Handler,可以直接使用getMainLooper()获取主线程Looper对象,并创建Handler实例。例如,在Activity中实现在子线程中更新UI:
new Thread(new Runnable() {@Overridepublic void run() {// 子线程中进行耗时操作...// 完成后在主线程中更新UImHandler.post(new Runnable() {@Overridepublic void run() {// UI更新代码}});}
}).start();
- 在子线程中使用Handler
在子线程中更新UI,需要将当前线程的Looper对象关联给Handler实例,以便于在消息队列中执行此消息。
new Thread(new Runnable() {@Overridepublic void run() { 在子线程中进行耗时操作...Message message = mHandler.obtainMessage(MSG_ID, arg1, arg2);mHandler.sendMessage(message);}
}).start();mHandler = new Handler(new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {switch (msg.what) {case MSG_ID:// UI更新代码return true;default:return false;}}
});
- Handler常见任务
3.1 延迟执行任务
使用Handler.postDelayed()方法可将消息延迟一段时间后再发送到消息队列中。例如:
mHandler.postDelayed(new Runnable() {@Overridepublic void run() {//执行延迟任务}
}, DELAY_MILLIS);
3.2 周期性执行任务
使用Handler.postDelayed()方法和Handler.sendMessage()方法结合,可实现周期性执行任务的功能。
mHandler.postDelayed(new Runnable() {@Overridepublic void run() {// 周期性执行任务mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_ID), INTERVAL);}
}, DELAY_MILLIS);
3.3 多任务按顺序执行
多任务按顺序串行运行,可以利用消息队列处理多个消息。
mHandler.sendMessage(Message.obtain(mHandler, TASK1_ID, arg1, arg2));
mHandler.sendMessage(Message.obtain(mHandler, TASK2_ID, arg1, arg2));
mHandler.sendMessage(Message.obtain(mHandler, TASK3_ID, arg1, arg2));
在Handler的handleMessage()方法中分别处理每个消息。
Handler调用方式的异同
使用Handler,有两种方法将消息加入消息队列中:post()方法和sendMessage()方法。两种方法的异同如下:
4.1 同异步方式
- post()方法是同步方式。即加入消息到消息队列中后,会直接处理此消息,不必等待消息阻塞的处理程序返回。
- sendMessage()方法是异步方式。即加入消息到消息队列中后,不会立即执行此消息,而是等待消息阻塞的处理程序返回。
4.2 方法参数
- post()方法的参数是Runnable对象,而Runnable对象中run()方法是待执行的任务代码。
- sendMessage()方法的参数是Message对象,而Message对象中的what字段是消息ID,arg1和arg2是可传递给处理程序的参数。
4.3 延迟时间参数
- postDelayed()方法是将Runnable对象放入到消息队列中,延迟指定时间执行,其第二个参数为DelayMillis。而post()方法不支持延迟时间参数。
- sendMessageDelayed()方法是将Message对象放入到消息队列中,延迟指定时间执行,其第二个参数为DelayMillis。而sendMessage()方法不支持延迟时间参数。
Handler的优缺点
5.1 优点
- 提供了方便的UI线程更新的方法。
- 避免了创建新线程带来的线程切换开销。
- 通过消息队列的方式,提高了线程的响应能力。
5.2 缺点
- Handler发送的消息会保存在消息队列中,如果一直发送大量的消息,将可能导致消息队列过长,影响应用的响应能力。
- LiveData和RxJava等现在比较流行的框架,能够替代Handler实现更优异的异步编程和UI更新。
总结
Handler是Android系统的核心组件,在线程与消息传递中发挥着重要的作用。它可以帮助用户在不同的线程中实现消息的传递和任务的执行,并且提供了许多具有实际用途的方法,例如post()、sendMessage()、postDelayed()、sendMessageDelayed()等等。但是,Handler也有其缺点,比如常规使用可能会导致消息队列过度拥挤
相关文章:
带你深入了解Android Handler的用法
Android中,Handler是一类用于异步消息传递和线程之间通信的基础框架。一个Handler是一个线程的处理器,可以接收消息,并调度运行它们。使用Handler,应用程序可以将处理器与一个线程关联,以将来的时间运行任务。而使用Ha…...

生于零售的亚马逊云科技,如何加速中国跨境电商企业出海?
导读:跨境电商进入精耕细作的新阶段。 作为中国企业出海的重要领域之一,近几年跨境电商行业处在快速发展中。商务部数据显示,2022年中国跨境电商出口达1.55万亿,同比增长11.7%。2023年1-2月,跨境电商进出口总额同比增长…...
兄弟组件传值$on无法接收值
方法一 前提是必须引入EventBus,而且该方法一刷新数据就没了 1.组件A里,点击事件里面使用$emit传入数据 2.组件A里,mounted里面使用$on接收数据,并把数据赋给EventBus EventBus.$on(detail,(data) > { EventBus.senddata d…...

Spring事务及事务传播机制
一.事务的含义:多个操作封装在一起,要么同时执行成功,一旦有一个操作执行失败,那么全部执行失败。这里给大家举个例子:比如A给B转账50元,而B没有收到这50元,此时A转账B这个操作也需要进行回滚,恢复到A给B没…...
npm i 常见问题
需要注意的是,如果你在使用 NPM 安装包的过程中遇到了任何问题,可以尝试使用 --verbose 参数打印更详细的错误信息,以便更好地诊断问题。例如: npm install --verbose 1、vue老项目缺少编译环境安装依赖报错的问题 待下载的项目…...

Prometheus+Grafana监控系统
一、简介 1、Prometheus简介 官网:https://prometheus.io 项目代码:https://github.com/prometheus Prometheus(普罗米修斯)是一个最初在SoundCloud上构建的监控系统。自2012年成为社区开源项目,拥有非常活跃的开发人员…...

基于脉冲神经网络的物体检测
访问【WRITE-BUG数字空间】_[内附完整源码和文档] 研究的意义在于探索脉冲神经网络在目标检测上的应用,目前主流的脉冲神经网络训练算法有直接BP训练、STDP无监督训练和训练好的ANN的转化,虽然训练算法众多,但是SNN仍然没有一套成熟的训练算…...

Rust每日一练(Leetday0010) 子串下标、两数相除、串联子串
目录 28. 找出字符串中第一个匹配项的下标 Find-the-index-of-the-first-occurrence-in-a-string 🌟🌟 29. 两数相除 Divide Two Integers 🌟🌟 30. 串联所有单词的子串 Substring-with-concatenation-of-all-words &#x…...
As ccess 数据库与表的操作
1. Access 数据库设计的一般步骤 . 2. 基本概念:Access 数据库、表、记录、字段 . 3. 使用表设计器创建表 (1)字段名命名规则 不能空格开头、不能用.!()[]、最长 64 个字符 (2)字段类型:文本、数字、日期/时…...
自动化的测试工具
1, 自动化功能测试工具:QTP、selenium 2, 自动化性能测试功能:LoadRunner、jmeter 3, 自动化接口测试工具:Charles、soapUI、LoadRunner、jmeter、postman、 测试工具 4, 测试管理工…...

Host头攻击
转载与:https://blog.csdn.net/weixin_47723270/article/details/129472716 01 HOST头部攻击漏洞知识 Host首部字段是HTTP/1.1新增的,旨在告诉服务器,客户端请求的主机名和端口号,主要用来实现虚拟主机技术。 运用虚拟主机技术&a…...
Android 12.0默认开启无障碍服务权限和打开默认apk无障碍服务
1.概述 在12.0的系统rom定制化开发中,在第三方app开发中,需要开启无障碍服务功能,就不需要在代码中开启无障碍服务了, 为了简便就需要在系统中开启无障碍服务,来实现开启无障碍服务功能 2. 默认开启无障碍服务权限和打开默认apk无障碍服务核心代码 frameworks/base/core…...
怎么成为优秀的软件工程师,而不是优秀的码农?
作为软件行业的从业者,每个人都希望最终成为优秀的软件工程师,而不仅仅是码农。一个码农只关注于编写代码和解决问题,而一个软件工程师则涉及到更广泛的职责和技能。 以下是一些要点,可以帮助你脱颖而出,成为一个优秀…...

安装ElasticSearch之前的准备工作jdk的安装
一.windows 下载jdk的软件 (1).进入jdk1.8官网 (2).根据电脑是32位还是64位按需下载 (3).点击下载之后就会跳转到Oracle账号登录界面 没有 Oracle账号的注册一下就可以了 下载好的jdk如下: 双击下一步下一步安装jdk 默认安装就可以了 配置环境变量 (1).电脑左下方设置选项 (2).…...

复杂数据集,召回、精度等突破方法记录【以电科院过检识别模型为参考】
目录 一、数据分析与数据集构建 二、所有相关的脚本 三、模型效果 一、数据分析与数据集构建 由于电科院数据集有17w-18w张,标签错误的非常多,且漏标非常多,但是所有有效时间只有半个月左右,显卡是M60,训练速度特别…...
那些你不得不会的提高工作效率的软件神器
那些你不得不会的提高工作效率的软件神器 文本编辑器 vscode 跨平台,插件丰富。 code-server vscode服务器版本,可以在浏览器中开发调试代码,尤其适用于windows端开发linux服务器程序。 vim linux/unix/mac终端最强大的文本编辑器。 note…...

【VMware】Ubunt 20.04时间设置
文章目录 设置本地时间 UTC8设置24小时制同步网络时间参考 Talk is cheap, show me the code. 设置本地时间 UTC8 查看当前时区状态 rootnode1:~/k8s# timedatectlLocal time: Sun 2023-05-21 15:24:02 CSTUniversal time: Sun 2023-05-21 07:24:02 UTCRTC time: Sun 2023-05-2…...

单点登录三:添加RBAC权限校验模型功能理解及实现demo
1、RBAC权限模型 RBAC(Role-Based Access Control)是一种基于角色的访问控制模型,用于管理系统中用户的权限和访问控制。它将用户、角色和权限之间的关系进行了明确的定义,以实现灵活的权限管理和控制。 1.1、RBAC模型主要包括以…...
基于用户认证数据构建评估模型预测认证行为风险系统(附源码)
文件说明 datasets // 数据集(训练集、测试集) feature engineering // 特征工程 models // 评估模型 测试环境 Python3.8 任务描述 项目来自系统认证风险预测https://www.datafountain.cn/competitions/537 项目完整源码下载:https://download.csdn.net/download/liu…...
本地训练中文LLaMA模型实战教程,民间羊驼模型,24G显存盘它!
羊驼实战系列索引 博文1:本地部署中文LLaMA模型实战教程,民间羊驼模型 博文2:本地训练中文LLaMA模型实战教程,民间羊驼模型(本博客) 博文3:精调训练中文LLaMA模型实战教程,民间羊驼模型(马上发布) 简介 在学习完上篇【1本地部署中文LLaMA模型实战教程,民间羊驼模…...

Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
#Uniapp篇:chrome调试unapp适配
chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器:Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...

接口自动化测试:HttpRunner基础
相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具,支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议,涵盖接口测试、性能测试、数字体验监测等测试类型…...

计算机基础知识解析:从应用到架构的全面拆解
目录 前言 1、 计算机的应用领域:无处不在的数字助手 2、 计算机的进化史:从算盘到量子计算 3、计算机的分类:不止 “台式机和笔记本” 4、计算机的组件:硬件与软件的协同 4.1 硬件:五大核心部件 4.2 软件&#…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能
1. 开发环境准备 安装DevEco Studio 3.1: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK 项目配置: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...

【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...

C++_哈希表
本篇文章是对C学习的哈希表部分的学习分享 相信一定会对你有所帮助~ 那咱们废话不多说,直接开始吧! 一、基础概念 1. 哈希核心思想: 哈希函数的作用:通过此函数建立一个Key与存储位置之间的映射关系。理想目标:实现…...