前言:我们知道在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. Git 常用命令速查表(图文+表格)
  2. 【错误记录】Flutter 报错 ( Could not resolve io.flutter:flutter_embedding_debug:1.0.0. )
  3. redis实例python_生产消费者模式与python+redis实例运用(基础篇)
  4. 谁说产品经理和程序员之间不能和平共处?
  5. 管理感悟:一种人才分类
  6. 2018第七届iWeb峰会城市巡回——杭州站
  7. MES(制造企业生产过程执行管理系统)
  8. 登陆界面HTML验证码生成
  9. Nexys video ftp搭建和传输
  10. mysql数据库隔离级别
  11. CTS测试时手机的设置
  12. php jsapi支付没有走回调,微信支付成功没有执行回调路径
  13. Unity3D脚本中文教程(八)
  14. android ios打包工具下载,IOS移动开发之快速打包工具---- iTunes 降级 到12.6,回到你熟悉的版本...
  15. usaco Horseshoes
  16. PS Cloud:Odoo在中国唯一的SaaS营销平台
  17. 以太坊2.0区块链代码将于下个月最终确定
  18. Matlab数组内插(平滑)函数
  19. 郑州远程教育的计算机和英语统考,郑州大学网络远程教育统考大学英语B6练习题...
  20. Codeforce D. Ceil Divisions (构造+思维)

热门文章

  1. 我害怕阅读的人[与其兜售价值,不如兜售恐惧]
  2. Microsoft Edge 从老版本升级到chromium 内核后,打开所有网页报此页存在问题
  3. Excel - VBA实例: 字符串截取有效内容
  4. C# 使用委托实现异步编程的四种方式
  5. 【Laravel 】faker数据填充详解
  6. 详解c# Emit技术
  7. 网易公开课 matlab,数学专业各学科视频网站【珍藏版】
  8. 读文章笔记(十一):对比学习(Contrastive Learning)
  9. 乐高 计算机泡泡龙教案,泡泡龙的秘密小班科学教案
  10. rssi java_关于RSSI的问题