浅谈HandlerThread
前言:我们知道在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相关推荐
- 浅谈MySQL存储引擎-InnoDBMyISAM
浅谈MySQL存储引擎-InnoDB&MyISAM 存储引擎在MySQL的逻辑架构中位于第三层,负责MySQL中的数据的存储和提取.MySQL存储引擎有很多,不同的存储引擎保存数据和索引的方式 ...
- 【大话设计模式】——浅谈设计模式基础
初学设计模式给我最大的感受是:人类真是伟大啊!单单是设计模式的基础课程就让我感受到了强烈的生活气息. 个人感觉<大话设计模式>这本书写的真好.让貌似非常晦涩难懂的设计模式变的生活化.趣味化 ...
- 学校计算机机房好处,浅谈学校计算机机房维护
浅谈学校计算机机房维护 现在的学校机房都配置了数量较多的计算机,而且机房的使用非常频繁.对于怎样维护好计算机,特别是计算机软件系统,对广大计算机教师来说是一个很重要且非常现实的问题.下面就本人在 ...
- java 中的单元测试_浅谈Java 中的单元测试
单元测试编写 Junit 单元测试框架 对于Java语言而言,其单元测试框架,有Junit和TestNG这两种, 下面是一个典型的JUnit测试类的结构 package com.example.dem ...
- mybatis与php,浅谈mybatis中的#和$的区别
浅谈mybatis中的#和$的区别 发布于 2016-07-30 11:14:47 | 236 次阅读 | 评论: 0 | 来源: 网友投递 MyBatis 基于Java的持久层框架MyBatis 本 ...
- 浅谈GCC预编译头技术
浅谈GCC预编译头技术 文/jorge --谨以此文,悼念我等待MinGW编译时逝去的那些时间. 其 实刚开始编程的时候,我是丝毫不重视编译速度之类的问题的,原因很简单,因为那时我用BASICA.后来 ...
- 【笔记】震惊!世上最接地气的字符串浅谈(HASH+KMP)
震惊!世上最接地气的字符串浅谈(HASH+KMP) 笔者过于垃圾,肯定会有些错的地方,欢迎各位巨佬指正,感激不尽! 引用:LYD的蓝书,一本通,DFC的讲稿,网上各路巨佬 Luguo id: 章鱼那个 ...
- 浅谈几种区块链网络攻击以及防御方案之其它网络攻击
旧博文,搬到 csdn 原文:http://rebootcat.com/2020/04/16/network_attack_of_blockchain_other_attack/ 写在前面的话 自比特 ...
- 浅谈几种区块链网络攻击以及防御方案之拒绝服务攻击
旧博文,搬到 csdn 原文:http://rebootcat.com/2020/04/14/network_attack_of_blockchain_ddos_attack/ 写在前面的话 自比特币 ...
最新文章
- Git 常用命令速查表(图文+表格)
- 【错误记录】Flutter 报错 ( Could not resolve io.flutter:flutter_embedding_debug:1.0.0. )
- redis实例python_生产消费者模式与python+redis实例运用(基础篇)
- 谁说产品经理和程序员之间不能和平共处?
- 管理感悟:一种人才分类
- 2018第七届iWeb峰会城市巡回——杭州站
- MES(制造企业生产过程执行管理系统)
- 登陆界面HTML验证码生成
- Nexys video ftp搭建和传输
- mysql数据库隔离级别
- CTS测试时手机的设置
- php jsapi支付没有走回调,微信支付成功没有执行回调路径
- Unity3D脚本中文教程(八)
- android ios打包工具下载,IOS移动开发之快速打包工具---- iTunes 降级 到12.6,回到你熟悉的版本...
- usaco Horseshoes
- PS Cloud:Odoo在中国唯一的SaaS营销平台
- 以太坊2.0区块链代码将于下个月最终确定
- Matlab数组内插(平滑)函数
- 郑州远程教育的计算机和英语统考,郑州大学网络远程教育统考大学英语B6练习题...
- Codeforce D. Ceil Divisions (构造+思维)
热门文章
- 我害怕阅读的人[与其兜售价值,不如兜售恐惧]
- Microsoft Edge 从老版本升级到chromium 内核后,打开所有网页报此页存在问题
- Excel - VBA实例: 字符串截取有效内容
- C# 使用委托实现异步编程的四种方式
- 【Laravel 】faker数据填充详解
- 详解c# Emit技术
- 网易公开课 matlab,数学专业各学科视频网站【珍藏版】
- 读文章笔记(十一):对比学习(Contrastive Learning)
- 乐高 计算机泡泡龙教案,泡泡龙的秘密小班科学教案
- rssi java_关于RSSI的问题