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

Android HandlerThread 基础

HandlerThread

    • **一、HandlerThread的基本概念和用途**
      • 1. **目的**
      • 2. **与普通线程的区别**
    • **二、HandlerThread的使用步骤**
      • 1. **创建HandlerThread对象并启动线程**
      • 2. **创建Handler并关联到HandlerThread的消息队列**
      • 3. **发送消息到HandlerThread的消息队列**
    • **三、HandlerThread的生命周期和注意事项**
      • 1. **生命周期**
      • 2. **注意事项**
    • 四、使用 HandlerThread 和 线程池 举同一个例子
      • 1. **使用HandlerThread的示例:下载文件并更新UI**
      • 2. **使用线程池的示例:下载文件并更新UI(同样的功能)**
      • 3.例子1中的 handler 不在主线程了么
  • 参考地址

HandlerThread是Android中的一个类,它继承自Thread,主要用于在一个单独的线程中处理消息队列(MessageQueue)。以下是关于它的详细内容:

一、HandlerThread的基本概念和用途

1. 目的

  • 在Android开发中,为了避免在主线程(UI线程)执行耗时操作而导致应用程序出现“ANR(Application Not Responding)”的情况,需要将一些耗时任务(如网络请求、文件读写等)放到后台线程中执行。
  • HandlerThread提供了一种方便的方式来创建一个带有消息队列的后台线程。(和handler一起配合使用达到)
  • 它允许通过Handler发送消息到该线程的消息队列中,然后在该线程中按照消息发送的顺序依次处理这些消息。这样就可以在一个单独的线程中有序地执行一系列任务。

2. 与普通线程的区别

  • 普通线程没有自带的消息队列机制。如果要在普通线程中处理多个任务,需要自己实现任务调度和排队等复杂的逻辑。而HandlerThread内部已经实现了消息队列,并且可以通过Handler方便地与其他线程进行通信。

  • 例如,在一个普通线程中,如果要处理多个不同类型的任务,可能需要使用复杂的状态机或者阻塞队列等方式来管理任务。但是HandlerThread通过消息机制(MessageMessageQueue),可以很方便地通过sendMessage等方法发送任务请求,并且在HandlerhandleMessage方法中处理这些任务。

二、HandlerThread的使用步骤

1. 创建HandlerThread对象并启动线程

  • 首先,需要创建一个HandlerThread对象。例如:
HandlerThread handlerThread = new HandlerThread("MyHandlerThread");
handlerThread.start();
  • 这里创建了一个名为MyHandlerThreadHandlerThread,然后调用start方法来启动这个线程。启动后,该线程就会开始运行,并且创建一个与之关联的消息队列。

2. 创建Handler并关联到HandlerThread的消息队列

  • 接着,需要创建一个Handler对象,并将其与HandlerThread的消息队列关联起来。可以通过以下方式实现:
Handler handler = new Handler(handlerThread.getLooper()) {@Overridepublic void handleMessage(Message msg) {// 在这里处理消息switch (msg.what) {case 1:// 执行任务1break;case 2:// 执行任务2break;}}
};
  • 这里通过handlerThread.getLooper()获取HandlerThreadLooper对象。Looper是一个用于循环获取消息队列中的消息并分发给Handler的类。通过这种方式,创建的Handler就可以将消息发送到HandlerThread的消息队列中,并且在handleMessage方法中处理这些消息。

3. 发送消息到HandlerThread的消息队列

  • 最后,可以通过Handler发送消息到HandlerThread的消息队列中。例如:
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
  • 这里创建了一个Message对象,设置了消息的what属性(用于区分不同类型的消息),然后通过handler.sendMessage方法将消息发送到HandlerThread的消息队列中。HandlerThread中的Looper会不断地从消息队列中获取消息,并将消息分发给关联的HandlerhandleMessage方法进行处理。

三、HandlerThread的生命周期和注意事项

1. 生命周期

  • HandlerThread对象被创建并调用start方法后,线程开始运行,消息队列被创建,Looper开始循环获取消息。
  • 只要还有未处理的消息在消息队列中,或者Looper没有被显式地退出,HandlerThread就会一直运行。可以通过调用HandlerThreadquit或者quitSafely方法来退出Looper,从而结束HandlerThread的运行。例如:
handlerThread.quitSafely();
  • quitSafely方法会在处理完当前消息队列中的已有消息后退出Looper,而quit方法会立即退出Looper,可能会导致消息丢失。

2. 注意事项

  • 内存泄漏:如果Handler对象是一个内部类,并且它间接引用了外部类(例如Activity)的实例,而HandlerThread的生命周期又比外部类长,那么可能会导致外部类无法被垃圾回收,从而引起内存泄漏。为了避免这种情况,可以将Handler定义为静态内部类,并使用弱引用(WeakReference)来引用外部类实例。
  • 消息处理顺序:HandlerThread中的消息是按照发送的顺序依次处理的。如果有高优先级的任务,需要在消息机制的基础上进行适当的调整,例如可以通过设置消息的优先级或者在handleMessage方法中根据任务的紧急程度优先处理某些消息。

四、使用 HandlerThread 和 线程池 举同一个例子

1. 使用HandlerThread的示例:下载文件并更新UI

  • 布局文件(activity_main.xml)
    • 简单的布局包含一个按钮用于触发下载和一个文本视图用于显示下载状态。
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><Buttonandroid:id="@+id/download_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="下载文件"/><TextViewandroid:id="@+id/status_text"android:layout_width="wrap_content"android:layout_height="wrap_content"/>
    </LinearLayout>
    
  • Java代码(MainActivity.java)
    • MainActivity中实现下载文件的功能。
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.HandlerThread;
    import android.os.Message;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TextView;
    import androidx.appcompat.app.AppCompatActivity;
    public class MainActivity extends AppCompatActivity {private HandlerThread handlerThread;private Handler handler;private TextView statusText;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button downloadButton = findViewById(R.id.download_button);statusText = findViewById(R.id.status_text);// 创建HandlerThread并启动handlerThread = new HandlerThread("DownloadThread");handlerThread.start();// 创建Handler并关联到HandlerThread的消息队列handler = new Handler(handlerThread.getLooper()) {@Overridepublic void handleMessage(Message msg) {if (msg.what == 1) {// 模拟下载文件的过程try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}// 下载完成后发送消息到主线程更新UIMessage uiMessage = new Message();uiMessage.what = 2;uiHandler.sendMessage(uiMessage);}}};downloadButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// 发送下载文件的消息到HandlerThreadMessage downloadMessage = new Message();downloadMessage.what = 1;handler.sendMessage(downloadMessage);statusText.setText("正在下载...");}});// 创建用于更新UI的主线程HandlerHandler uiHandler = new Handler(getMainLooper()) {@Overridepublic void handleMessage(Message msg) {if (msg.what == 2) {statusText.setText("下载完成");}}};}@Overrideprotected void onDestroy() {super.onDestroy();// 退出HandlerThreadhandlerThread.quitSafely();}
    }
    
    • 首先,在onCreate方法中创建HandlerThread并启动它,然后创建与HandlerThread消息队列关联的Handler。当用户点击下载按钮时,发送一个消息到HandlerThread的消息队列,在handleMessage方法中模拟文件下载过程(这里通过Thread.sleep来模拟耗时操作)。下载完成后,发送一个消息到主线程的Handler来更新UI,显示下载完成的状态。最后,在onDestroy方法中退出HandlerThread

2. 使用线程池的示例:下载文件并更新UI(同样的功能)

  • 布局文件(与上面相同,activity_main.xml)
  • Java代码(MainActivity.java)
    import android.os.Bundle;
    import android.os.Handler;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TextView;
    import androidx.appcompat.app.AppCompatActivity;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    public class MainActivity extends AppCompatActivity {private ExecutorService executorService;private TextView statusText;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button downloadButton = findViewById(R.id.download_button);statusText = findViewById(R.id.status_text);// 创建一个单线程的线程池(这里可以根据需要调整线程池大小)executorService = Executors.newSingleThreadExecutor();downloadButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {statusText.setText("正在下载...");// 提交下载任务到线程池executorService.submit(new Runnable() {@Overridepublic void run() {try {// 模拟下载文件的过程Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}// 下载完成后发送消息到主线程更新UIHandler uiHandler = new Handler(getMainLooper()) {@Overridepublic void handleMessage(Message msg) {if (msg.what == 1) {statusText.setText("下载完成");}}};Message uiMessage = new Message();uiMessage.what = 1;uiHandler.sendMessage(uiMessage);}});}});}@Overrideprotected void onDestroy() {super.onDestroy();// 关闭线程池executorService.shutdown();}
    }
    
    • 这里在onCreate方法中创建一个单线程的线程池ExecutorService。当用户点击下载按钮时,将下载任务以Runnable的形式提交到线程池。在Runnablerun方法中模拟文件下载过程,下载完成后,创建一个主线程的Handler,并发送消息到主线程来更新UI,显示下载完成的状态。最后,在onDestroy方法中关闭线程池。这两种方式都实现了在后台执行下载任务,避免阻塞主线程,并且在任务完成后更新UI的功能。

3.例子1中的 handler 不在主线程了么

  1. handler不在主线程中
    • 在第一个例子中,handler关联的是HandlerThreadLooper,而HandlerThread是一个单独的后台线程。当创建handler时,通过handlerThread.getLooper()获取的是HandlerThread线程中的Looper对象。
    • 例如,代码中的这部分:
    handler = new Handler(handlerThread.getLooper()) {@Overridepublic void handleMessage(Message msg) {if (msg.what == 1) {// 模拟下载文件的过程try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}// 下载完成后发送消息到主线程更新UIMessage uiMessage = new Message();uiMessage.what = 2;uiHandler.sendMessage(uiMessage);}}
    };
    
    • 这个handlerhandleMessage方法是在HandlerThread线程中执行的。HandlerThread提供了一个后台线程环境,这样就可以在这个线程中执行一些耗时的操作,比如模拟文件下载(通过Thread.sleep来模拟耗时),而不会阻塞主线程。
  2. 与主线程通信的uiHandler
    • 为了更新UI,又创建了一个uiHandler,它是关联到主线程(UI线程)的Looper的。
    • 代码如下:
    Handler uiHandler = new Handler(getMainLooper()) {@Overridepublic void handleMessage(Message msg) {if (msg.what == 2) {statusText.setText("下载完成");}}
    };
    
    • 当后台HandlerThread中的任务完成后,通过uiHandler发送消息到主线程的消息队列,然后在主线程中执行uiHandlerhandleMessage方法来更新UI。这是因为在Android中,只有主线程才能更新UI,所以需要这种跨线程通信的方式来在后台任务完成后更新界面显示。

参考地址

豆包 AI

相关文章:

Android HandlerThread 基础

HandlerThread **一、HandlerThread的基本概念和用途**1. **目的**2. **与普通线程的区别** **二、HandlerThread的使用步骤**1. **创建HandlerThread对象并启动线程**2. **创建Handler并关联到HandlerThread的消息队列**3. **发送消息到HandlerThread的消息队列** **三、Handl…...

【智能算法应用】人工水母搜索算法求解二维路径规划问题

摘要 本文基于人工水母搜索算法&#xff08;Jellyfish Search Algorithm, JSA&#xff09;&#xff0c;对二维路径规划问题进行了研究。JSA作为一种新兴的群体智能优化算法&#xff0c;模仿了水母在海洋中觅食和迁移的行为&#xff0c;以求解非线性、复杂的优化问题。实验结果…...

【Altium】原理图如何利用参数管理器批量修改元器件属性

【更多软件使用问题请点击亿道电子官方网站】 1、 文档目标 解决在使用AD设计原理图的时候&#xff0c;使用参数管理器批量修改元器件的属性。 2、 问题场景 客户在使用ad时&#xff0c;想大批量修改元器件的属性&#xff0c;类似于Cadence中&#xff0c;批量修改Manufactur…...

基于Spring Boot与Redis的令牌主动失效机制实现

目录 前言1. 项目结构和依赖配置1.1 项目依赖配置1.2 Redis连接配置 2. 令牌主动失效机制的实现流程2.1 登录成功后将令牌存储到Redis中2.2 使用拦截器验证令牌2.3 用户修改密码后删除旧令牌 3. Redis的配置与测试4. 可能的扩展与优化结语 前言 在现代Web系统中&#xff0c;用…...

深度学习之循环神经网络(RNN)

1 为什么需要RNN&#xff1f; ​ 时间序列数据是指在不同时间点上收集到的数据&#xff0c;这类数据反映了某一事物、现象等随时间的变化状态或程度。一般的神经网络&#xff0c;在训练数据足够、算法模型优越的情况下&#xff0c;给定特定的x&#xff0c;就能得到期望y。其一…...

Autosar CP Network Management模块规范导读

Network Management模块的主要功能 网络管理适配:作为通信管理器和总线特定网络管理模块之间的适配层,实现不同总线网络管理功能的统一接口,确保系统中各种网络的协同工作。协调功能 网络协调关闭:使用协调算法协调多个网络的关闭,确保它们在合适的时间同步进入睡眠模式,…...

Xshell 7 偏好设置

1 Xshell7 工具——更改用户数据文件夹 就是此电脑目录下的文档 该目录下的7 Xshell下的 applog ColorScheme Files 配色方案文件目录 HighlightSet Files 突出显示集目录 Logs 日志 QuickButton Files 快速命令集 Scripts 脚本文件 Sessions 会话文件 会话文件目录就…...

云计算答案

情境一习题练习 一、选择题 1、在虚拟机VMware软件中实现联网过程&#xff0c;图中箭头所指的网络连接方式与下列哪个相关&#xff08; C &#xff09;。 A.仅主机模式 B.桥接 C.NAT D.嫁接 2、请问下图这个虚拟化架构属于什么类型&#xff08; A …...

浅谈现货白银与白银td的价格差异

西方资本主义世界崇尚自由经济&#xff0c;而我国实行社会主义市场经济&#xff0c;因此二者在金融系统上存在不少差异&#xff0c;反映在贵金属市场中&#xff0c;可能直接表现为价格上的差异。如果投资者对此能有基本的了解&#xff0c;日后面对交易中的特殊价格波动&#xf…...

【QT常用技术讲解】任务栏图标+socket网络服务+开机自启动

前言 首先看网络编程的定义&#xff1a;两个不同主机设备之间的进程通信。C/S(Client-Server)是早期非常典型的软件架构&#xff0c;C/S架构虽然简单&#xff0c;但却非常适用于桌面图形化的QT项目。 本篇的QT项目是从真实的项目中简化出来&#xff0c;满足很多相似的场景&…...

【计算机基础——数据结构——AVL平衡二叉树】

1. BST二叉查找树 1.1 BST二叉查找树的特性 左子树上所有结点的值均小于或等于它的根结点的值。右子树上所有结点的值均大于或等于它的根结点的值。左、右子树也分别为二叉排序树。 1.2 BST二叉查找树的缺点 二叉查找树是有缺点的&#xff0c;在不断插入的时候&#xff0c;…...

体育活动赛事报名马拉松微信小程序开发

功能描述 体育活动赛事报名马拉松微信小程序&#xff0c;该项目是一个体育活动报名小程序&#xff0c;主要功能有活动报名、扫码签到、签到积分、排行奖励、积分兑换等功能。 用户端&#x1f536;登录&#xff1a;◻️1.微信授权登录 ◻️2.手机号码授权 &#x1f536;首页&am…...

【C++】C++基础知识

一.函数重载 1.函数重载的概念 函数重载是函数的一种特殊情况&#xff0c;C允许在同一作用域中声明几个功能类似的同名函数&#xff0c;这些同名函数的形参列表必须不同。函数重载常用来处理实现功能类似&#xff0c;而数据类型不同的问题。 #include <iostream> using…...

中间件安全

IIS IIS短文件漏洞 此漏洞实际是由HTTP请求中旧DOS 8.3名称约定(SFN)的代字符(~)波浪号引起的。它允许远程攻击者在Web根目录下公开文件和文件夹名称(不应该可被访问)。攻击者可以找到通常无法从外部直接访问的重要文件,并获取有关应用程序基础结构的信息。 利用工具 https…...

Zabbix中文监控指标数据乱码

1&#xff09;点击主机&#xff0c;选择Zabbix server 中的 图形 一项&#xff0c;可以看到当前显示的为乱码 2&#xff09; 下载字体文件&#xff1a; https://gitcode.com/open-source-toolkit/4a3db/blob/main/SimHei.zip 解压unzip -x SimHei.zip 3&#xff09; 替换字体文…...

【AI】AI如何赋能软件开发流程

方向一&#xff1a;流程与模式介绍【传统软件开发 VS AI参与的软件开发】 传统软件开发流程 传统软件开发流程一般可以分为以下几个阶段&#xff1a; 1. 需求分析&#xff1a;在这个阶段&#xff0c;开发团队与客户沟通&#xff0c;明确软件的需求和目标。团队会收集、整理和分…...

恒创科技:什么是 RAID 3 ? RAID 3、4 和5之间有什么区别?

RAID 是一种存储数据以提高性能并减少数据丢失的特定技术。您可以根据自己的需求选择多种 RAID 类型。RAID 3 是列表中比较有效的类型之一。本文将重点介绍这种特定的 RAID 技术&#xff0c;并比较 RAID 3、4 和 5。 RAID 3 的定义 RAID 3 是一种特定的磁盘配置&#xff0c;用于…...

python获取iOS最近业务日志的两种方法

当iOS UI自动化用例执行失败的时候&#xff0c;需要获取当时的业务日志&#xff0c;供后续分析使用。 现在已经把iOS沙盒目录挂载到本地&#xff0c;剩下的事情就是从沙盒目录中捞取当前的日志&#xff0c;沙盒中的日志文件较大&#xff0c;整体导出来也可以&#xff0c;但是会…...

【如何获取股票数据43】Python、Java等多种主流语言实例演示获取股票行情api接口之沪深指数历史交易数据获取实例演示及接口API说明文档

最近一两年内&#xff0c;股票量化分析逐渐成为热门话题。而从事这一领域工作的第一步&#xff0c;就是获取全面且准确的股票数据。因为无论是实时交易数据、历史交易记录、财务数据还是基本面信息&#xff0c;这些数据都是我们进行量化分析时不可或缺的宝贵资源。我们的主要任…...

ESLint 使用教程(一):从零配置 ESLint

前言 在现代前端开发中&#xff0c;代码质量和风格一致性是团队合作和项目维护的重要因素。而 ESLint 作为一款强大的 JavaScript 静态代码分析工具&#xff0c;能够帮助开发者发现和修复代码中的潜在问题。本文将详细介绍 ESLint 的常用规则配置&#xff0c;并结合实际应用场…...

Python|GIF 解析与构建(5):手搓截屏和帧率控制

目录 Python&#xff5c;GIF 解析与构建&#xff08;5&#xff09;&#xff1a;手搓截屏和帧率控制 一、引言 二、技术实现&#xff1a;手搓截屏模块 2.1 核心原理 2.2 代码解析&#xff1a;ScreenshotData类 2.2.1 截图函数&#xff1a;capture_screen 三、技术实现&…...

第19节 Node.js Express 框架

Express 是一个为Node.js设计的web开发框架&#xff0c;它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用&#xff0c;和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…...

数据链路层的主要功能是什么

数据链路层&#xff08;OSI模型第2层&#xff09;的核心功能是在相邻网络节点&#xff08;如交换机、主机&#xff09;间提供可靠的数据帧传输服务&#xff0c;主要职责包括&#xff1a; &#x1f511; 核心功能详解&#xff1a; 帧封装与解封装 封装&#xff1a; 将网络层下发…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

Linux离线(zip方式)安装docker

目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1&#xff1a;修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本&#xff1a;CentOS 7 64位 内核版本&#xff1a;3.10.0 相关命令&#xff1a; uname -rcat /etc/os-rele…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

android13 app的触摸问题定位分析流程

一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...