前言:我们知道在Android系统中,我们执行完耗时操作都要另外开启子线程来执行,执行完线程以后线程会自动销毁。想象一下如果我们在项目中经常要执行耗时操作,如果经常要开启线程,接着又销毁线程,这无疑是很消耗性能的?那有什么解决方法呢?

一般有两种:

  • 使用线程池
  • 使用HandlerThread

  而HandlerThread是android系统帮我们封装的一个异步处理任务的Thread,并且内部封装了looper对象。

HandlerThread特点:

  • HandlerThread本质上是一个线程类,它继承了Thread;
  • HandlerThread有自己的内部Looper对象,可以进行looper循环;
  • 通过获取HandlerThread的looper对象传递给Handler对象,可以在handleMessage方法中执行异步任务。
  • 创建HandlerThread后必须先调用HandlerThread.start()方法,Thread会先调用run方法,创建Looper对象。

一、HandlerThread常规使用步骤


1.创建实例对象

HandlerThread handlerThread = new HandlerThread("downloadImage");
  • 1

2.启动HandlerThread线程

handlerThread.start();
  • 1

3.构建循环消息处理机制

  /*** 该callback运行于子线程*/
class ChildCallback implements Handler.Callback {@Overridepublic boolean handleMessage(Message msg) {//在子线程中进行相应的网络请求//通知主线程去更新UImUIHandler.sendMessage(msg1);return false;}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

4.构建异步handler

Handler childHandler = new Handler(handlerThread.getLooper(),new ChildCallback());
  • 1

二、HandlerThread源码解析


  先贴出完整的源码,HandlerThread的源码非常精简,只有不到100行:

public class HandlerThread extends Thread {int mPriority;int mTid = -1;Looper mLooper;public HandlerThread(String name) {super(name);mPriority = Process.THREAD_PRIORITY_DEFAULT;}public HandlerThread(String name, int priority) {super(name);mPriority = priority;}/*** Call back method that can be explicitly overridden if needed to execute some* setup before Looper loops.*/protected void onLooperPrepared() {}@Overridepublic void run() {mTid = Process.myTid();Looper.prepare();//持有锁机制来获得当前线程的Looper对象synchronized (this) {mLooper = Looper.myLooper();//发出通知,当前线程已经创建mLooper对象成功,这里主要是通知getLooper方法中的waitnotifyAll();}//设置线程的优先级别Process.setThreadPriority(mPriority);//这里默认是空方法的实现,我们可以重写这个方法来做一些线程开始之前的准备,方便扩展onLooperPrepared();Looper.loop();mTid = -1;}public Looper getLooper() {if (!isAlive()) {return null;}// 直到线程创建完Looper之后才能获得Looper对象,Looper未创建成功,阻塞synchronized (this) {while (isAlive() && mLooper == null) {try {wait();} catch (InterruptedException e) {}}}return mLooper;}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;}/*** Returns the identifier of this thread. See Process.myTid().*/public int getThreadId() {return mTid;}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81

1.先分析构造方法

public HandlerThread(String name) {super(name);mPriority = Process.THREAD_PRIORITY_DEFAULT;}public HandlerThread(String name, int priority) {super(name);mPriority = priority;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

  可以看出HandlerThread继续自Thread,有两个构造方法,构造方法的传递参数分别有一个和有两个,一个参数的构造方法的参数是name指的是线程的名称;两个参数的构造方法的参数一个是name指的是线程的名称,一个是priority指的是线程优先级;我们根据需要调用即可。

    int mPriority;//线程优先级int mTid = -1;Looper mLooper;//当前线程持有的Looper对象protected void onLooperPrepared() {} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

  其中成员变量mLooper就是HandlerThread自己持有的Looper对象。onLooperPrepared()该方法是一个空实现,是留给我们必要时可以去重写的,但是注意重写时机是在Looper循环启动前。

2.再分析run方法

 @Override
public void run() {mTid = Process.myTid();Looper.prepare();synchronized (this) {mLooper = Looper.myLooper();notifyAll(); //唤醒等待线程}Process.setThreadPriority(mPriority);onLooperPrepared();Looper.loop();mTid = -1;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

  在创建HandlerThread对象后必须调用其start()方法才能进行其他操作,而调用start()方法后相当于启动了线程,也就是run方法将会被调用。因为HandlerThread是继承与Thread类的。

  从代码我们可以看到Looper.prepare(),这里Looper对象被创建,这样我们再构建异步handler时才可以把Looper对象给Handler对象,进而确保Handler对象中的handleMessage方法是在异步线程执行的。

接着执行

synchronized (this) {mLooper = Looper.myLooper();notifyAll(); //唤醒等待线程}
  • 1
  • 2
  • 3
  • 4

这里唤醒了等待线程,原因就要看看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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

  先用isAlive()判断线程是否启动,不然线程没启动就不存在Looper,自然返回空(null),如果判断true进行下个判断,Looper的创建是在子线程中执行的,而调用getLooper方法则是在主线程进行的,无法保证在调用getLooper方法时Looper已经被创建,所以要判断mLooper == null,是空的话就让getLooper方法先等待,让run方法创建完成之后notifyAll()来唤醒线程,这就是notifyAll()的原因了。

3.最后看看quit和quitSafely方法

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;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

  quit方法实际是调用Looper的quit方法,该方法主要是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送)还是非延迟消息。

  quitSafely方法实际是调用Looper的quitSafely方法,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理完成后才停止Looper循环,quitSafely相比于quit方法安全的原因在于清空消息之前会派发所有的非延迟消息。

三、总结


  HandlerThread对管理线程的开启和销毁做出了自己的封装,并且可以通过getLooper方法来传给Handler,可以在handleMessage方法中执行异步任务。避免了频繁开启多个线程和handler组合消耗大量性能的问题。

浅谈HandlerThread相关推荐

  1. 浅谈MySQL存储引擎-InnoDBMyISAM

    浅谈MySQL存储引擎-InnoDB&MyISAM 存储引擎在MySQL的逻辑架构中位于第三层,负责MySQL中的数据的存储和提取.MySQL存储引擎有很多,不同的存储引擎保存数据和索引的方式 ...

  2. 【大话设计模式】——浅谈设计模式基础

    初学设计模式给我最大的感受是:人类真是伟大啊!单单是设计模式的基础课程就让我感受到了强烈的生活气息. 个人感觉<大话设计模式>这本书写的真好.让貌似非常晦涩难懂的设计模式变的生活化.趣味化 ...

  3. 学校计算机机房好处,浅谈学校计算机机房维护

    浅谈学校计算机机房维护    现在的学校机房都配置了数量较多的计算机,而且机房的使用非常频繁.对于怎样维护好计算机,特别是计算机软件系统,对广大计算机教师来说是一个很重要且非常现实的问题.下面就本人在 ...

  4. java 中的单元测试_浅谈Java 中的单元测试

    单元测试编写 Junit 单元测试框架 对于Java语言而言,其单元测试框架,有Junit和TestNG这两种, 下面是一个典型的JUnit测试类的结构 package com.example.dem ...

  5. mybatis与php,浅谈mybatis中的#和$的区别

    浅谈mybatis中的#和$的区别 发布于 2016-07-30 11:14:47 | 236 次阅读 | 评论: 0 | 来源: 网友投递 MyBatis 基于Java的持久层框架MyBatis 本 ...

  6. 浅谈GCC预编译头技术

    浅谈GCC预编译头技术 文/jorge --谨以此文,悼念我等待MinGW编译时逝去的那些时间. 其 实刚开始编程的时候,我是丝毫不重视编译速度之类的问题的,原因很简单,因为那时我用BASICA.后来 ...

  7. 【笔记】震惊!世上最接地气的字符串浅谈(HASH+KMP)

    震惊!世上最接地气的字符串浅谈(HASH+KMP) 笔者过于垃圾,肯定会有些错的地方,欢迎各位巨佬指正,感激不尽! 引用:LYD的蓝书,一本通,DFC的讲稿,网上各路巨佬 Luguo id: 章鱼那个 ...

  8. 浅谈几种区块链网络攻击以及防御方案之其它网络攻击

    旧博文,搬到 csdn 原文:http://rebootcat.com/2020/04/16/network_attack_of_blockchain_other_attack/ 写在前面的话 自比特 ...

  9. 浅谈几种区块链网络攻击以及防御方案之拒绝服务攻击

    旧博文,搬到 csdn 原文:http://rebootcat.com/2020/04/14/network_attack_of_blockchain_ddos_attack/ 写在前面的话 自比特币 ...

最新文章

  1. 构建node.js基础镜像_在Android上构建Node.js应用程序
  2. 什么是分布式系统!以及分布式系统架构的优缺点!
  3. 3.3V与5V的电平转换
  4. 常考数据结构与算法-NC105 二分查找-II
  5. 文本处理三剑客之 awk
  6. think php框架 跨域,ThinkPHP框架实现session跨域问题
  7. 【Ubuntu】ubuntu系统下python3和python2环境自由切换
  8. 二十年后我发明了保姆机器人作文_机器人保姆我的发明作文450字
  9. SpringMVC接收Post的实体/JSon数据
  10. OneNote 2013 快捷键
  11. 被骗好多年:原来这才是大数据
  12. RDMA相关的技术网站
  13. EF中关于TransactionScope的使用
  14. MySQL索引类型详解,让MySQL高效运行起来
  15. Linux(CentOS) 下安装字体
  16. 研磨java_研磨设计模式.pdf
  17. android 反色 java_安卓实现图片反色,改色-ColorMatrixColorFilter
  18. js 跳转到指定位置 高德地图_JS控制div跳转到指定的位置的几种解决方案总结
  19. 『IT视界』 [职场人生]从软件工程师到IT猎头续:告诉你如何写简历
  20. ai 计算机视觉_人工智能中的计算机视觉

热门文章

  1. OpenText 解说免费迁移工具带有的隐性成本和不必要的复杂性
  2. 计算机操作系统:实验2 【银行家算法】
  3. 不用第三方工具解压缩msi文件提取文件
  4. PhotoShop使用记录
  5. vue3+elementPlus主题动态切换2022,亲测可用!
  6. Python字符串转换为小写字母
  7. 利用python对图像进行傅里叶变换_python实现图像傅里叶变换
  8. 解决 SSH 连接速度慢
  9. VxWorks驱动移植基础知识
  10. 【特征选择】基于二元多邻域人工蜂群 (BMNABC) 特征选择问题附matlab代码