HandlerThread源码分析

2024-02-03 05:58
文章标签 分析 源码 handlerthread

本文主要是介绍HandlerThread源码分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

HandlerThread是拿来做什么的呢?

我们知道Handler是用来异步更新UI的,更详细的说是用来做线程间的通信的,更新UI时是子线程UI主线程之间的通信。那么现在我们要是想子线程与子线程之间的通信要怎么做呢?当然说到底也是用Handler+Thread来完成,现在只不过Google官方很贴心的帮我们封装好了一个类HandlerThread

关于HandlerThreadLooper之间的故事可以参考这篇文章Android线程间通信的消息机制(Android开发艺术探索学习笔记),看完之后再来了解HandlerThread那就很容易了。

话不多说,直接上源码:

/*** Handy class for starting a new thread that has a looper. The looper can then be * used to create handler classes. Note that start() must still be called.*/
public class HandlerThread extends Thread {}

HandlerThread继承自Thread,并且类上面的定义就直接告诉我们了,开启的HandlerThread会自带一个Looper
也就是说HandlerThread是自带Looper的线程。

那我们就去看run()

    @Overridepublic void run() {mTid = Process.myTid();Looper.prepare();synchronized (this) {mLooper = Looper.myLooper();notifyAll();}Process.setThreadPriority(mPriority);onLooperPrepared();Looper.loop();mTid = -1;}

可以看到该方法中调用了Looper.prepare()Looper.loop()

Looper.prepare()中创建了一个Looper对象,并且把该对象放到了该线程范围内的变量中(sThreadLocal),在Looper对象的构造过程中,初始化了一个MessageQueue,作为该Looper对象成员变量。

Looper.loop()就开启了轮询,不断的循环从MessageQueue中取消息然后去处理,当没有消息的时候会阻塞,有消息到来的时候会唤醒。

看过上面推荐的文章,以上的分析你应该就了解了。

接下来,我们来看一个实例,代码如下:

public class HandlerThreadActivity extends AppCompatActivity {private TextView mTvServiceInfo;private HandlerThread mCheckMsgThread;private Handler mCheckMsgHandler;private boolean isUpdateInfo;private static final int MSG_UPDATE_INFO = 0x110;//与UI线程管理的handlerprivate Handler mHandler = new Handler();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_thread_handler);//创建后台线程initBackThread();mTvServiceInfo = (TextView) findViewById(R.id.id_textview);}@Overrideprotected void onResume() {super.onResume();//开始查询isUpdateInfo = true;mCheckMsgHandler.sendEmptyMessage(MSG_UPDATE_INFO);}@Overrideprotected void onPause() {super.onPause();//停止查询isUpdateInfo = false;mCheckMsgHandler.removeMessages(MSG_UPDATE_INFO);}private void initBackThread() {mCheckMsgThread = new HandlerThread("check-message-coming");mCheckMsgThread.start();mCheckMsgHandler = new Handler(mCheckMsgThread.getLooper()) {@Overridepublic void handleMessage(Message msg) {checkForUpdate();if (isUpdateInfo) {mCheckMsgHandler.sendEmptyMessageDelayed(MSG_UPDATE_INFO, 1000);}}};}/*** 模拟从服务器解析数据*/private void checkForUpdate() {try {//模拟耗时Thread.sleep(1000);mHandler.post(new Runnable() {@Overridepublic void run() {String result = "实时更新中,当前大盘指数:<font color='red'>%d</font>";result = String.format(result, (int) (Math.random() * 3000 + 1000));mTvServiceInfo.setText(Html.fromHtml(result));}});} catch (InterruptedException e) {e.printStackTrace();}}@Overrideprotected void onDestroy() {super.onDestroy();//释放资源mCheckMsgThread.quit();}
}

布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"android:paddingBottom="@dimen/activity_vertical_margin"><TextViewandroid:id="@+id/id_textview"android:text="正在加载大盘指数..."android:layout_width="wrap_content"android:layout_height="wrap_content"/></RelativeLayout>

可以看到我们在onCreate()中,去创建和启动了HandlerThread,并且关联了一个mCheckMsgHandler。然后我们分别在onResume()onPause()中去开启和暂停我们的查询,最后在onDestory()中去释放资源。

这样就实现了我们每隔5s去服务端查询最新的数据,然后更新我们的UI,当然我们这里通过Thread.sleep()模拟耗时,返回了一个随机数,大家可以很轻易的换成真正的数据接口。

在例子中,有一段代码是这样的:

        mCheckMsgThread = new HandlerThread("check-message-coming");mCheckMsgThread.start();mCheckMsgHandler = new Handler(mCheckMsgThread.getLooper()) {@Overridepublic void handleMessage(Message msg) {checkForUpdate();if (isUpdateInfo) {mCheckMsgHandler.sendEmptyMessageDelayed(MSG_UPDATE_INFO, 1000);}}};

我们通过HandlerThreadgetLooper()得到mCheckMsgThread Looper,并作为参数传入Handler的构造函数,然后我们就为子线程mCheckMsgThread 创建了一个mCheckMsgHandler

这就来看一下getLooper()

    public Looper getLooper() {if (!isAlive()) {return null;}// If the thread has been started, wait until the looper has been created.synchronized (this) {while (isAlive() && mLooper == null) {try {wait();} catch (InterruptedException e) {}}}return mLooper;}

run()里面当mLooper创建完成后有个notifyAll()getLooper()中有个wait(),这是为什么呢?

因为mLooper的创建是在子线程mCheckMsgThread 中执行,而我们的mCheckMsgHandler 是在主线程初始化的,也就是说,我们必须等到mLooper创建完成,才能正确的返回getLooper()notifyAll()wait()就是为了解决这两个线程的同步问题。

总结
HandlerThread类是一个专门处理Handler消息的线程,依次从Handler的队列中获取信息,逐个进行处理,保证安全,不会出现混乱引发的异常。HandlerThread继承于Thread,所以它本质就是个Thread。与普通Thread的差别就在于,它有个Looper成员变量。

参考:Android HandlerThread 完全解析

这篇关于HandlerThread源码分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/673169

相关文章

Android实现一键录屏功能(附源码)

《Android实现一键录屏功能(附源码)》在Android5.0及以上版本,系统提供了MediaProjectionAPI,允许应用在用户授权下录制屏幕内容并输出到视频文件,所以本文将基于此实现一个... 目录一、项目介绍二、相关技术与原理三、系统权限与用户授权四、项目架构与流程五、环境配置与依赖六、完整

Android实现定时任务的几种方式汇总(附源码)

《Android实现定时任务的几种方式汇总(附源码)》在Android应用中,定时任务(ScheduledTask)的需求几乎无处不在:从定时刷新数据、定时备份、定时推送通知,到夜间静默下载、循环执行... 目录一、项目介绍1. 背景与意义二、相关基础知识与系统约束三、方案一:Handler.postDel

慢sql提前分析预警和动态sql替换-Mybatis-SQL

《慢sql提前分析预警和动态sql替换-Mybatis-SQL》为防止慢SQL问题而开发的MyBatis组件,该组件能够在开发、测试阶段自动分析SQL语句,并在出现慢SQL问题时通过Ducc配置实现动... 目录背景解决思路开源方案调研设计方案详细设计使用方法1、引入依赖jar包2、配置组件XML3、核心配

Java NoClassDefFoundError运行时错误分析解决

《JavaNoClassDefFoundError运行时错误分析解决》在Java开发中,NoClassDefFoundError是一种常见的运行时错误,它通常表明Java虚拟机在尝试加载一个类时未能... 目录前言一、问题分析二、报错原因三、解决思路检查类路径配置检查依赖库检查类文件调试类加载器问题四、常见

Python中的Walrus运算符分析示例详解

《Python中的Walrus运算符分析示例详解》Python中的Walrus运算符(:=)是Python3.8引入的一个新特性,允许在表达式中同时赋值和返回值,它的核心作用是减少重复计算,提升代码简... 目录1. 在循环中避免重复计算2. 在条件判断中同时赋值变量3. 在列表推导式或字典推导式中简化逻辑

Java程序进程起来了但是不打印日志的原因分析

《Java程序进程起来了但是不打印日志的原因分析》:本文主要介绍Java程序进程起来了但是不打印日志的原因分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java程序进程起来了但是不打印日志的原因1、日志配置问题2、日志文件权限问题3、日志文件路径问题4、程序

Java 正则表达式URL 匹配与源码全解析

《Java正则表达式URL匹配与源码全解析》在Web应用开发中,我们经常需要对URL进行格式验证,今天我们结合Java的Pattern和Matcher类,深入理解正则表达式在实际应用中... 目录1.正则表达式分解:2. 添加域名匹配 (2)3. 添加路径和查询参数匹配 (3) 4. 最终优化版本5.设计思

Java字符串操作技巧之语法、示例与应用场景分析

《Java字符串操作技巧之语法、示例与应用场景分析》在Java算法题和日常开发中,字符串处理是必备的核心技能,本文全面梳理Java中字符串的常用操作语法,结合代码示例、应用场景和避坑指南,可快速掌握字... 目录引言1. 基础操作1.1 创建字符串1.2 获取长度1.3 访问字符2. 字符串处理2.1 子字

Python 迭代器和生成器概念及场景分析

《Python迭代器和生成器概念及场景分析》yield是Python中实现惰性计算和协程的核心工具,结合send()、throw()、close()等方法,能够构建高效、灵活的数据流和控制流模型,这... 目录迭代器的介绍自定义迭代器省略的迭代器生产器的介绍yield的普通用法yield的高级用法yidle

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序