本文我们将学习HandlerThread的实现原理,以及开发时,如何正确的使用它来实现我们的开发任务。

HandlerThread源码分析


设想这样一个场景:我们要在一个线程A中处理业务逻辑,在另一个线程B中,监听A的执行,并进行结果处理。这时我们使用HandlerThread就可以非常简单的实现该功能了。

通常我们的线程交互场景,都是UI线程中启动子线程,并且由子线程完成工作任务,最终结果交给UI线程。现在我们的使用场景是,在子线程中监控其他线程的执行结果(这里的其他线程可以是另一个子线程,也可以是UI线程),并在子线程中进行结果的处理。

通过描述,我们可以得出2点结论:第一,这个过程中,需要存在2个线程;第二,这两个线程需要进行数据传输(交互)。那么,我们很自然的就想到了Handler机制来实现该功能,但是我们自己在一个子线程中,使用Handler稍显麻烦一些,HandlerThread内置了Handler,简化了我们的操作。

HandlerThread构造函数和继承关系:

public class HandlerThread extends Thread {int mPriority;int mTid = -1;Looper mLooper;private @Nullable Handler mHandler;public HandlerThread(String name) {//构造函数1super(name);mPriority = Process.THREAD_PRIORITY_DEFAULT;}/*** Constructs a HandlerThread.* @param name* @param priority The priority to run the thread at. The value supplied must be from * {@link android.os.Process} and not from java.lang.Thread.*/public HandlerThread(String name, int priority) {//构造函数2super(name);mPriority = priority;}
}
逻辑解析:
  1. HandlerThread继承自Thread类,所以它本质上是一个线程类,可以实现线程相关的操作。
  2. 我们来看HandlerThread的构造函数,这里有2个重载版本。
  3. 第一个构造函数接收一个name参数,直接调用了super方法,为该线程命令,且线程优先级为Process.THREAD_PRIORITY_DEFAULT。
  4. 第二个构造函数接收2个参数,可实现线程命名,和设置线程的优先级。

HandlerThread的run方法:

    @Overridepublic void run() {mTid = Process.myTid();Looper.prepare();synchronized (this) {mLooper = Looper.myLooper();notifyAll();}Process.setThreadPriority(mPriority);onLooperPrepared();Looper.loop();mTid = -1;}
逻辑解析:
  1. 我们在创建了HandlerThread实例之后,调用start()方法执行,ART虚拟机会帮我们创建一个线程对象,然后在子线程中执行run方法。
  2. run方法中,执行了Looper.prepare()方法,创建了一个Looper对象并绑定到该线程。
  3. 然后在同步块中,执行了mLooper的赋值,调用notifyAll通知Looper已创建完成。
  4. 调用Process.setThreadPriority方法设置线程优先级。
  5. 调用空方法onLooperPrepared,通常用于回调使用。
  6. 最后调用Looper.loop()方法,启动该线程的消息循环。

getLooper方法:

    public Looper getLooper() {if (!isAlive()) {//判断线程是否存活return null;}// If the thread has been started, wait until the looper has been created.synchronized (this) {//如果线程存活,但mLooper没有创建完成,则等待while (isAlive() && mLooper == null) {try {wait();} catch (InterruptedException e) {}}}return mLooper;//返回当前线程的Looper对象}
逻辑解析:
  1. getLooper方法可以返回当前线程绑定的Looper对象,我们可以使用该对象作为参数,创建一个该线程的Handler对象,用于线程交互。
  2. getLooper方法首先会判断当前线程是否存活,如果存活,则继续。
  3. 如果线程存活,但Looper对象还没有创建完成,则调用wait方法进行等待(Looper创建完成后,会在HandlerThread的run方法中,调用notifyAll()通知,以继续执行当前逻辑)。
  4. 最后返回Looper对象即可。

HandlerThread的getThreadHandler方法

    /*** @return a shared {@link Handler} associated with this thread* @hide*/public Handler getThreadHandler() {if (mHandler == null) {mHandler = new Handler(getLooper());}return mHandler;}
  1. 如果我们不想自己创建Handler对象,也可以使用HandlerThread为他们提供的Handler对象。
  2. getThreadHandler方法返回一个当前线程的Handler对象。
  3. 它是一个隐藏方法,我们在应用中不可调用。

HandlerThread的退出

    //立即执行退出public boolean quit() {Looper looper = getLooper();if (looper != null) {looper.quit();return true;}return false;}//处理完成已到执行时间的消息后退出。public boolean quitSafely() {Looper looper = getLooper();if (looper != null) {looper.quitSafely();return true;}return false;}

当不需要HandlerThread时,需要调用quit方法或quitSafely方法结束线程管理的Looper消息循环。

HandlerThread实战


HandlerThread的实现其实并不复杂,我们以一个简单Demo来看它的使用。

Demo

public class HandlerThreadTest {private final static String TAG = "budaye";public static final int HT_MSG = 1;private HandlerThread mHandlerThread = new HandlerThread("myHandlerThread", Process.THREAD_PRIORITY_BACKGROUND);private Handler mHandler = null;public void startHandlerthread(){mHandlerThread.start();if (mHandler == null){mHandler = new Handler(mHandlerThread.getLooper()){@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);switch (msg.what){case HT_MSG:Log.d(TAG, "当前线程:" + Thread.currentThread());Log.d(TAG, "收到其他线程发送过来的消息了");break;}}};}}public void sendMessage() {Log.d(TAG, "当前线程:" + Thread.currentThread());Message msg = Message.obtain();msg.what = HT_MSG;mHandler.sendMessage(msg);}
}
逻辑解析:
  1. 在HandlerThreadTest里,创建了一个HandlerThread对象。
  2. 调用startHandlerthread方法,开始了HandlerThread对象的线程消息循环,并且创建了一个mHandler对象,用于处理其他线程发送过来的消息。
  3. 我们可以在UI线程中调用sendMessage方法来发送给HandlerThread线程一个消息,并在mHandler对象的handleMessage方法中进行处理。

Demo日志输出

25208-25208/com.example.simpledemo D/budaye: 当前线程:Thread[main,5,main]
25208-25284/com.example.simpledemo D/budaye: 当前线程:Thread[myHandlerThread,5,main]
25208-25284/com.example.simpledemo D/budaye: 收到其他线程发送过来的消息了

我们分别在2个线程中输出了日志。

最佳实践&总结


  1. HandlerThread是一个异步处理的工具类,它可以帮助我们很轻松的实现异步线程处理。
  2. HandlerThread继承自Thread类,它的本质是一个线程类。
  3. HandlerThread实现原理非常简单,它利用了Handler原理,在内部了一个Looper循环,并绑定到当前线程中。
  4. 我们使用创建一个Handler对象,绑定到HandlerTHread对象所对应的Looper,并处理其他线程发送过来的消息。
  5. HandlerThread在构造方法中可以设置线程优先级,默认使用Process.THREAD_PRIORITY_DEFAULT作为默认优先级。
  6. 我们在应用过程中,不要大量使用HandlerThread来执行异步任务,这样会造成线程资源的浪费,大量的异步任务,建议使用线程池来进行操作。
  7. HandlerThread的线程优先级设定一定要注意,如果任务优先级不高,则应该设置成后台任务优先级,避免和UI线程抢占系统资源。

HandlerThread原理分析、实战、最佳实践!相关推荐

  1. Guava Cache 原理分析与最佳实践

    前言 在大部分互联网架构中 Cache 已经成为了必可不少的一环.常用的方案有大家熟知的 NoSQL 数据库(Redis.Memcached),也有大量的进程内缓存比如 EhCache .Guava ...

  2. 重磅综述:三万字长文读懂单细胞RNA测序分析的最佳实践教程 (原理、代码和评述)

    原文链接: https://www.embopress.org/doi/10.15252/msb.20188746 主编评语 这篇文章最好的地方不只在于推荐了工具,提供了一套分析流程,更在于详细介绍了 ...

  3. JAVA应用开发MQ实战最佳实践——Series2:消息队列RocketMQ性能测试案例

    简介:JAVA应用开发MQ实战最佳实践--Series2:消息队列RocketMQ性能测试案例 往期内容 JAVA应用开发MQ实战最佳实践--Series1:RocketMQ综述及代码设计 1. 消息 ...

  4. 阿里云数据库快速搭建疫情分析系统最佳实践

    简介:疫情降临,疫情态势分析和防控任务迫在眉睫,如果快速搭建高效的疫情态势分析系统是众多部门和单位的难题,阿里云RDS PG+Ganos解决方案可在极短时间内完成分析系统搭建,有效助力疫情防控. 直达 ...

  5. 超详细解读带你读懂单细胞RNA测序分析的最佳实践教程 (原理、代码和评述)

    Abstract 单细胞RNA-seq使研究者能够以前所未有的分辨率研究基因表达图谱.这一潜力吸引着更多科研工作者应用单细胞分析技术解决研究问题.随着可用的分析工具越来越多,如何组合成一个最新最好的数 ...

  6. mysql 两张大表关联_MySQL的DropTable影响分析和最佳实践

    [0.前言] MySQL上直接Drop张大表,会有什么影响,能否直接写个 drop table ; 或者 truncate table ; 甚至是delete * from? 如果这张表足够大,比如1 ...

  7. 在SDLC中使用静态代码分析的最佳实践

    http://vultrace.cn更多精彩,尽在个人博客. 文章翻译自ncc group的论文,论文超长预警,请耐心观看. Best Practices for the use of Static ...

  8. 数据治理分析项目最佳实践

    当今信息化建设程度不断深入,企业在优化整合各种IT能力,使IT成为企业的前进驱动力与核心竞争力的同时,将视角关注于更深层次的数据治理与分析,预示着以数据.流量.知识为主的数字经济时代到来,此背景下,数 ...

  9. 【送书福利-第七期】《分布式中间件核心原理与RocketMQ最佳实践》

    大家好,我是洲洲,欢迎关注,一个爱听周杰伦的程序员.关注公众号[程序员洲洲]即可获得10G学习资料.面试笔记.大厂独家学习体系路线等-还可以加入技术交流群欢迎大家在CSDN后台私信我! 本文目录 一. ...

最新文章

  1. Python基础实战之函数的参数讲解(三)
  2. mysql中engine=innodb和engine=myisam的区别 (转)
  3. openJDK之sun.misc.Unsafe类CAS底层实现
  4. sql 树状结构中知道 父节点与孙节点_集群环境中使用Zookeeper实现分布式幂等控制...
  5. 类和对象—对象特性—const修饰成员函数
  6. asp.net C# 实现上传Excel文件导入数据到SQL Server 数据库
  7. 转载爱哥自定义View系列--Paint详解
  8. 不同服务器数据库表连接查询修改,如何连接多个数据库,mysql中的服务器和查询两个表中的对方?...
  9. Tomcat服务脚本
  10. iPhone 12在二季度iPhone出货量中占63% 低于iPhone 11同期
  11. 视觉SLAM——ORB-SLAM2运行tum数据集,kitti数据集,euroc数据集
  12. posix 线程(一)
  13. CCleaner v5.83.9050版,免费的系统优化和隐私保护工具
  14. Java疯狂讲义读书笔记第五章
  15. 威联通如何备份文件服务器上,威联通NAS提供最佳的备份解决方案
  16. CEH学习笔记之渗透测试框架
  17. 雅虎48亿美元卖身Verizon,门户网站路在何方?
  18. [RK3399][Android7.1] 移植笔记 --- GT9XX系列Touch添加
  19. Skiller V3
  20. 异地如何在北京换驾照

热门文章

  1. 详细介绍外观设计专利的申请流程和费用
  2. 脚本检查iOS app证书过期时间信息
  3. 初中计算机课程教案模板,初中体育课电子表格教案模板(共12篇)
  4. 精彩纷呈金秋数码展,颐高数码国际电脑节开幕在即
  5. SQL中的CONSTRAINT(约束)用法总结
  6. (附源码)Node.js图书管理小程序的开发 毕业设计 250858
  7. ZigBee智能家居安防硬件设计
  8. samba文件共享(匿名用户共享、本地用户共享、windows+linux系统之间的共享)
  9. 保护自己电脑绝对不做黑客肉鸡
  10. 安卓案例-跟随手指运动的小球