Android Handler 的基本使用

1.前言

https://developer.android.google.cn/reference/android/os/Handler.html

Handler 是 Android 中线程通信的常用方式,文档如是说:

A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler it is bound to a Looper. It will deliver messages and runnables to that Looper's message queue and execute them on that Looper's thread.

Handler 允许你发送和处理与线程的 MessageQueue 关联的 Message 和 Runnable 对象。 每个 Handler 实例都与一个线程和该线程的消息队列 (MessageQueue) 相关联。 当你创建一个新的 Handler 时,它会绑定到 Looper。 它将向 Looper 的消息队列传递消息 (Message) 和可运行对象 (Runnable),并在 Looper 的线程上执行它们。

There are two main uses for a Handler: (1) to schedule messages and runnables to be executed at some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.

Handler 有两个主要用途: (1) 安排消息和可运行对象在未来某个时刻执行; (2) 将要在与你自己的线程不同的线程上执行的操作排队。

Scheduling messages is accomplished with the post(Runnable), postAtTime(java.lang.Runnable, long), postDelayed(Runnable, Object, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long) methods. The post versions allow you to enqueue Runnable objects to be called by the message queue when they are received; the sendMessage versions allow you to enqueue a Message object containing a bundle of data that will be processed by the Handler's handleMessage(Message) method (requiring that you implement a subclass of Handler).

消息的调度是通过 post(Runnable), postAtTime(java.lang.Runnable, long), postDelayed(Runnable, Object, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long) 等方法来完成的。post 系列的方法允许你将 Runnable 对象放入队列,当消息队列处理该消息时 Runnable 被调用;sendMessage 系列的方法允许你将可包含数据的 Message 对象放入队列,消息会被传递到 Handler 的 handleMessage(Message) 接口进行处理,你需要实现该接口。

When posting or sending to a Handler, you can either allow the item to be processed as soon as the message queue is ready to do so, or specify a delay before it gets processed or absolute time for it to be processed. The latter two allow you to implement timeouts, ticks, and other timing-based behavior.

当 post 或 sendMessage 到 Handler 时,可以在消息队列准备好后立即处理该消息,也可以指定延时时间 (Delayed) 或者指定时间点 (AtTime) 处理该消息。 后两者可以让你实现超时、定时和其他基于时间的行为。

When a process is created for your application, its main thread is dedicated to running a message queue that takes care of managing the top-level application objects (activities, broadcast receivers, etc) and any windows they create. You can create your own threads, and communicate back with the main application thread through a Handler. This is done by calling the same post or sendMessage methods as before, but from your new thread. The given Runnable or Message will then be scheduled in the Handler's message queue and processed when appropriate.

当为应用程序创建进程时,其主线程专用于运行一个消息队列,该消息队列负责管理顶层应用程序对象(Activity、BroadcastReceiver 等)及其创建的任何窗口。你可以创建自己的线程,并通过 Handler 与主应用程序线程进行通信。这只需要在新线程中调用 post 或 sendMessage 方法。 然后,给定的 Runnable 或 Message 将在 Handler 的消息队列中进行调度,并在适当的时候进行处理。

2.基本使用

2.1.基本流程

post 和 sendMessage 的基本使用,子线程向主线程发消息:

    // 创建 Handler 时关联一个 Looper 消息循环Handler handler = new Handler(Looper.getMainLooper()) {// 在 Looper 对应的线程处理该消息@Overridepublic void handleMessage(Message msg) {// 根据 Message what 区分不同的消息switch (msg.what) {case 0: {// 从 Message 中获取传递的参数数据进行处理// do some ... ...}break;}}};void doing() {// 在线程中执行任务new Thread(new Runnable() {@Overridepublic void run() {// do some ... ...// 任务完成 send 消息给 Handler 处理handler.sendEmptyMessage(0);// 或者 post 一个回调到 Handler 线程执行handler.post(new Runnable() {// Run 会在 Handler Looper 线程执行@Overridepublic void run() {// do some ... ...}});}}).start();}

2.2.Message 对象

创建 Message 的几种方式:

Message m1 = handler.obtainMessage(); //通过 Handler 实例获取
Message m2 = Message.obtain(); //通过 Message 获取
Message m3 = new Message(); //创建新的 Message 实例

其中,Handler 的 obtainMessage() 方法也是调用了 Message 的 obtain() 方法:

    public final Message obtainMessage() {return Message.obtain(this); // this Handler 作为 target 参数}public static Message obtain(Handler h) {Message m = obtain();m.target = h;return m;}

obtain()从消息池拿一个 Message 对象,不足时 new 一个新的 Message 返回:

    public static Message obtain() {synchronized (sPoolSync) {if (sPool != null) {Message m = sPool;sPool = m.next;m.next = null;m.flags = 0; // clear in-use flagsPoolSize--;return m;}}return new Message();}

Message 除了 what 属性可以用来区分消息类型,还可以携带参数:

Message msg = new Message();
// what:int 消息类型
msg.what = 1;
// arg1:int 和 arg2:int 两个 int 作为便捷参数,替代 obj
msg.arg1 = 0;
msg.arg2 = 0;
// obj:Object 存储对象
msg.obj = "Message";
// 存储复杂数据
// setData 设置内部 data,读取用 getData
Bundle bd = new Bundle();
bd.putString("name", "Bundle");
bd.putInt("code", 123);
msg.setData(bd);handler.sendMessage(msg);

2.3.向子线程发消息

Handler 需要关联一个 Looper 对象,主线程直接用 Looper.getMainLooper() 获取,子线程需要用 Looper.prepare() 创建 Looper,再用 Looper.loop() 开启消息循环。

    Handler handler;void test() {new Thread(new Runnable() {@Overridepublic void run() {Log.e(LogTag, "Looper in");// 准备 Looper,不存在则创建Looper.prepare();// 获取当前线程 Looperhandler = new Handler(Looper.myLooper()) {@Overridepublic void handleMessage(Message msg) {// do some ... ...Log.e(LogTag, "handleMessage " + msg.what);// 退出消息循环Looper.myLooper().quit();}};// 开启消息循环Looper.loop();Log.e(LogTag, "Looper exit");}}).start();new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(1000); // 延时,等 handler 初始化完// 发送消息Log.e(LogTag, "sendEmptyMessage");handler.sendEmptyMessage(123);} catch (InterruptedException e) {throw new RuntimeException(e);}}}).start();}

也可以用封装了 Looper 操作的 HandlerThread:

    Handler handler;HandlerThread thread;void test() {thread = new HandlerThread("test thread");thread.start();handler = new Handler(thread.getLooper()) {@Overridepublic void handleMessage(Message msg) {// do some ... ...Log.e(LogTag, "handleMessage " + msg.what);// 退出消息循环thread.quit();// 或者 thread.quitSafely();}};new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(1000); // 延时,等 handler 初始化完// 发送消息Log.e(LogTag, "sendEmptyMessage");handler.sendEmptyMessage(123);} catch (InterruptedException e) {throw new RuntimeException(e);}}}).start();}

3.内存泄漏 

界面 Activity finish 的时候,Handler 可能还被 MessageQueue 消息队列引用着。而前文 Handler 和 post 都用到了匿名内部类的写法:new Type(){},非静态的内部类和匿名内部类都会隐式持有外部类 Activity 的引用。Activity finish 不能立即回收,还要等 Handler 处理结束才能被回收,就造成了内存泄漏。

可以将 Handler 定义成静态内部类,静态内部类是不持有外部类的实例的。同时,为了能调用外部的实例方法,需要持有一个外部的弱引用。

public class MainActivity extends AppCompatActivity {private static class MyHandler extends Handler {// 弱引用持有 Activity, GC 回收时会被回收掉private WeakReference<MainActivity> weakReference;public MyHandler(MainActivity activity) {super(Looper.getMainLooper());this.weakReference = new WeakReference(activity);}@Overridepublic void handleMessage(Message msg) {MainActivity activity = weakReference.get();super.handleMessage(msg);if (null == activity) {return;}// do some ... ...}}private MyHandler handler;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// do some ... ...handler = new MyHandler(this);new Thread(new Runnable() {@Overridepublic void run() {handler.sendEmptyMessage(123);}}).start();}@Overrideprotected void onDestroy() {// 移除所有回调及消息handler.removeCallbacksAndMessages(null);super.onDestroy();}
}

4.参考

安卓文档:Handler  |  Android Developers (google.cn)

参考博客:Android——Handler详解_android handler_Yawn__的博客-CSDN博客

参考博客:Android - Handler_android handler looper_Jomurphys的博客-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/25181.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(十三)大数据实战——hadoop集群之YARN高可用实现自动故障转移

前言 本节内容是关于hadoop集群下yarn服务的高可用搭建&#xff0c;以及其发生故障转移的处理&#xff0c;同样需要依赖zookeeper集群的实现&#xff0c;实现该集群搭建时&#xff0c;我们要预先保证zookeeper集群是启动状态。yarn的高可用同样依赖zookeeper的临时节点及监控&…

构建器/建造者/构建者模式(C++)

定义 将一个复杂对象的构建与其表示相分离,使得同样的构建过程(稳定)可以创建不同的表示(变化)。 应用场景 在软件系统中&#xff0c;有时候面临着“一个复杂对象”的创建工作&#xff0c;其通常由各个部分的子对象用一定的算法构成;由于需求的变化&#xff0c;这个复杂对象…

淘宝API开发(一)简单介绍淘宝API功能接口作用

前一阵子按照上级指示&#xff0c;根据淘宝API开发符合自已应用的系统&#xff0c;比如批量上传&#xff0c;批量修改名称&#xff0c;价格等功能什么的&#xff0c;在此就将我的开发历程写一写&#xff0c;为自己前段时间的工作做个总结。 淘宝开发平台(淘宝网 - 淘&#xff…

Android应用开发(6)TextView进阶用法

Android应用开发学习笔记——目录索引 上一章Android应用开发&#xff08;5&#xff09;文本视图&#xff08;TextView&#xff09;介绍了文本视图&#xff08;TextView&#xff09;设置文本内容、设置文本大小、设置文本显示颜色。 TextView是最基础的文本显示控件&#xff…

PHP正则绕过解析

正则绕过 正则表达式PHP正则回溯PHP中的NULL和false回溯案例案例1案例2 正则表达式 在正则中有许多特殊的字符&#xff0c;不能直接使用&#xff0c;需要使用转义符\。如&#xff1a;$,(,),*,,.,?,[,,^,{。 这里大家会有疑问&#xff1a;为啥小括号(),这个就需要两个来转义&a…

Linux 下设置开机自启动的方法

文章目录 事先准备对于普通的 Linux对于 RedHat Enterprise Linux 9 笔者的运行环境&#xff1a; 设置成功过的 Linux&#xff1a; RedHat Enterprise Linux 9 x86_64 CentOS 8 x86_64 事先准备 进行这个教程之前&#xff0c;必须要先安装好一个 Linux 操作系统。这个 Linux…

JavaWeb 手写Tomcat底层机制

目录 一、Tomcat底层整体架构 1.简介 : 2.分析图 : 3.基于Socket开发服务端的流程 : 4.打通服务器端和客户端的数据通道 : 二、多线程模型的实现 1.思路分析 : 2.处理HTTP请求 : 3.自定义Tomcat : 三、自定义Servlet规范 1. HTTP请求和响应 : 1 CyanServletRequest …

《面试1v1》ElasticSearch基础

&#x1f345; 作者简介&#xff1a;王哥&#xff0c;CSDN2022博客总榜Top100&#x1f3c6;、博客专家&#x1f4aa; &#x1f345; 技术交流&#xff1a;定期更新Java硬核干货&#xff0c;不定期送书活动 &#x1f345; 王哥多年工作总结&#xff1a;Java学习路线总结&#xf…

工厂方法模式

工厂模式&#xff08;Factory Pattern&#xff09;是一种创建型设计模式&#xff0c;它提供了一种统一的接口来创建对象&#xff0c;但将对象的实例化延迟到子类中。工厂模式主要解决了对象的创建过程与使用客户端代码的解耦&#xff0c;使得客户端代码不需要知道具体的对象创建…

Kafka的配置和使用

目录 1.服务器用docker安装kafka 2.springboot集成kafka实现生产者和消费者 1.服务器用docker安装kafka ①、安装docker&#xff08;docker类似于linux的软件商店&#xff0c;下载所有应用都能从docker去下载&#xff09; a、自动安装 curl -fsSL https://get.docker.com | b…

Visual Studio配置PCL库

Visual Studio配置PCL库 Debug和Release配置新建项目配置属性表测试参考 Debug和Release Debug和Release的配置过程一模一样&#xff0c;唯一区别就在于最后一步插入的附加依赖项不同&#xff0c;因此下面以debug为例。 配置新建项目 1、新建一个C空项目&#xff0c;模式设置…

Linux文本三剑客之awk

目录 前言 awk 1.认识awk 2.使用awk 2.1语法 2.2常用命令选项 2.3awk变量 2.3.1内置变量 2.3.2自定义变量 2.4printf命令 awk例题 前言 awk、grep、sed是linux操作文本的三大利器&#xff0c;合称文本三剑客&#xff0c;也是必须掌握的linux命令之一。三者的功能都是…

3维空间下按平面和圆柱面上排版设计

AR空间中将若干平面窗口排列在指定平面或圆柱体面上 平面排版思路 指定平面方向向量layout_centre ,平面上的一点作为排版版面的中心layout_position float3 layout_position = float3(0,0,-10); float3 layout_centre = float3(0,0,1

FreeRTOS源码分析-9 互斥信号量

目录 1 优先级翻转问题 2 互斥信号量概念及其应用 2.2FreeRTOS互斥信号量介绍 2.3FreeRTOS互斥信号量工作原理 3 互斥信号量函数应用 3.1功能分析 3.2API详解 3.3功能实现 4 递归互斥信号量函数应用 4.1死锁现象 ​编辑 4.2API详解 4.3解决死锁 5 互斥信号量实现原…

C++数据结构之平衡二叉搜索树(一)——AVL的实现(zig与zag/左右双旋/3+4重构)

本文目录 00.BBST——平衡二叉搜索树01.AVL树02.AVL的插入2.1单旋——zig 与 zag2.2插入节点后的单旋实例2.3手玩小样例2.4双旋实例2.5小结 03.AVL的删除3.1单旋删除3.2双旋删除3.3小结 04.34重构05.综合评价AVL5.1优点5.2缺点 06.代码注意插入算法删除算法完整代码&#xff1a…

通过有名管道实现AB进程对话

一、要求实现AB进程对话 A进程先发送一句话给B进程&#xff0c;B进程接收后打印B进程再回复一句话给A进程&#xff0c;A进程接收后打印重复1.2步骤&#xff0c;当收到quit后&#xff0c;要结束AB进程 A进程 #include <stdio.h> #include <sys/types.h> #include…

Oracle EBS OM客制化调用API创建销售订单非常慢(FND_FLEX_HASH死锁)

业务场景 由于Oracle EBS标准功的公司间关联交易操作涉及业务节点环节多,需要多个业务部门参考操作完成,浪费人力和花费时间。随着国内集团公司通过业务整合优化,大幅度减少间中很多环节的人为操作,如国内公司间贸易通过类似于客制化出货单申请方式,跨国公司间贸易通过类似…

关于接口测试用例设计的一些思考

接口测试发现的典型问题 传入参数处理不当&#xff0c;引起程序错误类型溢出&#xff0c;导致数据读取和写入不一致对象权限校验出错&#xff0c;可获取其他角色信息状态出错&#xff0c;导致逻辑处理出现问题逻辑校验不完善定时任务执行出错 接口测试用例设计 接口测试用例…

redis入门3-在java中操作redis

Redis的java客户端 Jedis、Lettuce、Redisson、以及spring提供的spring data redis Jedis操作redis //添加依赖 <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.8.0</version> </dep…

JJWT快速入门

本篇介绍使用 JJWT&#xff08;Java JWT&#xff09;库来生成 JWT Token&#xff0c;步骤如下&#xff1a; 添加依赖&#xff1a; 在项目中添加 JJWT 依赖项。对于 Maven 项目&#xff0c;可以在 pom.xml 文件中添加以下依赖项&#xff1a; <dependency><groupId>…