当前位置: 首页 > news >正文

带你深入了解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 进行处理的方式有两种:

  1. 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();
  1. 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的核心流程如下:

  1. 首先,创建一个Handler实例,并与当前线程的Looper关联。
Handler mHandler = new Handler(Looper.getMainLooper());
  1. 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);
  1. 当消息被加入到消息队列后,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的基本用法

  1. 在主线程中使用Handler

在主线程中使用Handler,可以直接使用getMainLooper()获取主线程Looper对象,并创建Handler实例。例如,在Activity中实现在子线程中更新UI:

new Thread(new Runnable() {@Overridepublic void run() {// 子线程中进行耗时操作...// 完成后在主线程中更新UImHandler.post(new Runnable() {@Overridepublic void run() {// UI更新代码}});}
}).start();
  1. 在子线程中使用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;}}
});
  1. 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模型实战教程,民间羊驼模…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...

golang循环变量捕获问题​​

在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下: 问题背景 看这个代码片段: fo…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...

cf2117E

原题链接&#xff1a;https://codeforces.com/contest/2117/problem/E 题目背景&#xff1a; 给定两个数组a,b&#xff0c;可以执行多次以下操作&#xff1a;选择 i (1 < i < n - 1)&#xff0c;并设置 或&#xff0c;也可以在执行上述操作前执行一次删除任意 和 。求…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

laravel8+vue3.0+element-plus搭建方法

创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题

分区配置 (ptab.json) img 属性介绍&#xff1a; img 属性指定分区存放的 image 名称&#xff0c;指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件&#xff0c;则以 proj_name:binary_name 格式指定文件名&#xff0c; proj_name 为工程 名&…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)

考察一般的三次多项式&#xff0c;以r为参数&#xff1a; p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]&#xff1b; 此多项式的根为&#xff1a; 尽管看起来这个多项式是特殊的&#xff0c;其实一般的三次多项式都是可以通过线性变换化为这个形式…...

CSS | transition 和 transform的用处和区别

省流总结&#xff1a; transform用于变换/变形&#xff0c;transition是动画控制器 transform 用来对元素进行变形&#xff0c;常见的操作如下&#xff0c;它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...