尊重原创,转载请注明出处:http://blog.csdn.net/a740169405/article/details/50257001

HandlerThread概述

  1. HandlerThread是一个线程类
  2. HandlerThread内部创建了一个消息队列,其他线程可以通过Handler向其发送消息。
  3. HandlerThread按照消息的发送顺序依次对消息进行处理。
  4. HandlerThread在所有消息执行完毕后,线程并不会自动退出,消息队列任然在等待新任务。

一、HandlerThread内部消息队列的初始化

在线程成功执行后,HandlerThread创建了一个消息队列,用来接收其他线程发来的消息:

@Override
public void run() {mTid = Process.myTid();// 为当前线程创建LooperLooper.prepare();synchronized (this) {// 获取当前线程Looper对象mLooper = Looper.myLooper();// 唤醒其他等待线程notifyAll();}Process.setThreadPriority(mPriority);// mLooper对象初始化完成后回调onLooperPrepared();// 开始循环读取消息队列里的消息Looper.loop();mTid = -1;
}

该方法调用了Looper.prepare();创建了一个消息队列。这样,该线程就能接收handler发送的消息了。
最后调用了Looper.loop();开始循环读取消息。

二、向HandlerThread发送消息

@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 创建线程,命名为"myHandlerThread"HandlerThread handlerThread = new HandlerThread("myHandlerThread");// 启动线程handlerThread.start();// 创建Handler,并指定使用HandlerThread的消息队列来处理消息MyHandler myHandler = new MyHandler(handlerThread.getLooper());// 发送一条空消息给消息队列myHandler.obtainMessage().sendToTarget();}public static class MyHandler extends Handler {public MyHandler(Looper looper) {super(looper);}@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);// 收到消息后,打印出当前线程的名字Log.e(TAG, "current Thread name : " + Thread.currentThread().getName());}
}

我在主线程中初始化了一个HandlerThread线程,并启动了该线程。
向HandlerThread的消息队列发送了一条消息,消息收到后输出了当前线程的名字。

demo.chen.com.myapplication E/HandlerThreadActivity: current Thread name : myHandlerThread

后台输出的名字正是我创建线程的时候传递进去的。
通过上面的例子我们发现,向handlerThread发送的消息,是由HandlerThread线程来处理的。

三、HandlerThread锁机制:

大家注意这一段代码:

synchronized (this) {mLooper = Looper.myLooper();notifyAll();
}

加锁是为了确保其他现成获取该线程的Looper时不为空。
我们再看看HandlerThread提供的获取Looper的方法:

    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;}

代码的意思很简单,如果Looper还未创建,则进入等待状态,直到Looper被创建。

我尝试写一个HandlerThread的子类来验证一下:

public static class MyHandlerThread extends HandlerThread {public MyHandlerThread(String name) {super(name);}@Overridepublic void run() {try {// 在线程启动后,初始化消息队列之前,sleep三秒Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}// 调用父类的run方法初始化消息队列super.run();}@Overridepublic Looper getLooper() {return super.getLooper();}
}

逻辑很简单,重写run方法,在调用父类run方法进行Looper初始化之前让线程sleep三秒。
添加一些Log:

MyHandlerThread handlerThread = new MyHandlerThread("myHandlerThread");
// 线程启动前,先打个log记录当前时间
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Log.e(TAG, "current time = " + formatter.format(new Date()));
handlerThread.start();
Looper looper = handlerThread.getLooper();
// 在getLooper();之后打印log记录当前时间
Log.e(TAG, "get looper success, send message, time = " + formatter.format(new Date()));
MyHandler myHandler = new MyHandler(looper);
// 发送消息
myHandler.obtainMessage().sendToTarget();

再看看log:

E/HandlerThreadActivity: current time = 2015-12-11 08:45:03
E/HandlerThreadActivity: get looper success, send message, time = 2015-12-11 08:45:06
E/HandlerThreadActivity: current Thread name : myHandlerThread

从log中可以看出主线程等待了三秒,之所以加锁是为了防止在looper未被初始化之前,其他线程调用getLooper方法得到空对象。

注意:

一般我们是不需要重写HandlerThread类,只需要使用Handler对象向HandlerThread的消息队列发送消息,在handleMessage函数里处理消息,就可以了,且处理消息的线程是HandlerThread这个线程。

上面的例子中,我在消息队列初始化之前sleep了3秒,导致了主线程在getLooper的时候等待了3秒,也就是说有可能造成主线程ANR。

四、HandlerThread的退出

需要注意的是,一旦执行Looper.loop();线程便进入循环获取消息状态,也就是说线程不会自己停止,我们可以一直往线程发送消息,如果需要停止线程,只要调用HandlerThread对象的quit();或者quitSafely();函数,这两个函数其实只是调用了Looper对象的quit();或者quitSafely();函数:
HandlerThread的quit();函数:

public boolean quit() {// 获取当前线程的LooperLooper looper = getLooper();if (looper != null) {// 立即停止Looper的循环,不在处理未处理完的消息looper.quit();return true;}return false;
}

HandlerThread的quitSafely();函数:

public boolean quitSafely() {// 获取当前线程的LooperLooper looper = getLooper();if (looper != null) {// 打上需要停止循环的标志,等到消息都处理完以后,再推出循环looper.quitSafely();return true;}return false;
}

这两者的区别在于,调用quit();会直接终止消息队列的循环,如果队列中还有其他消息未处理,将不会被处理。
而调用quitSafely();函数则是给消息队列打上退出循环的标记,在所有消息处理完成以后,Looper自动退出循环,从而线程也就会结束。

总结:

1. HandlerThread类是google提供的一个,在线程内创建消息循环队列的线程API,让除了主线程以外的子线程也具有消息处理能力。
2. 使用时记得先调用HandlerThread的run方法启动线程
3. 在创建Handler时使用HandlerThread的Looper对象。
4. 确认所有任务都处理完成以后,记得调用HandlerThread推出函数,建议使用quitSafely();这样会等到所有消息都处理完后再推出线程。

应用:
HandlerThread在IntentService中应用到。
关于IntentService的项管内容,大家可以阅读我的另一片博客:Android IntentService的使用与源码解析

android HandlerThread源码解析相关推荐

  1. Android Lifecycle源码解析(一)

    Android Lifecycle源码解析(一) 首先我们看HomeActivity中我们添加到一行代码 public class HomeActivity extends AppCompatActi ...

  2. 【Android】Android Broadcast源码解析

    Android Broadcast源码解析 一.静态广播的注册 静态广播是通过PackageManagerService在启动的时候扫描已安装的应用去注册的. 在PackageManagerServi ...

  3. Android xUtils3源码解析之图片模块

    本文已授权微信公众号<非著名程序员>原创首发,转载请务必注明出处. xUtils3源码解析系列 一. Android xUtils3源码解析之网络模块 二. Android xUtils3 ...

  4. 【Android】Android Parcelable 源码解析

    Android Parcelable 源码解析 大家都知道,要想在Intent里面传递一些非基本类型的数据,有两种方式,一种实现Parcelable,另一种是实现Serializable接口.今天先不 ...

  5. Android xUtils3源码解析之注解模块

    本文已授权微信公众号<非著名程序员>原创首发,转载请务必注明出处. xUtils3源码解析系列 一. Android xUtils3源码解析之网络模块 二. Android xUtils3 ...

  6. Android xUtils3源码解析之数据库模块

    本文已授权微信公众号<非著名程序员>原创首发,转载请务必注明出处. xUtils3源码解析系列 一. Android xUtils3源码解析之网络模块 二. Android xUtils3 ...

  7. [Android] Handler源码解析 (Java层)

    之前写过一篇文章,概述了Android应用程序消息处理机制.本文在此文基础上,在源码级别上展开进行概述 简单用例 Handler的使用方法如下所示: Handler myHandler = new H ...

  8. android sdk 源码解析

    AndroidSdkSourceAnalysis:https://github.com/LittleFriendsGroup/AndroidSdkSourceAnalysis 第一期 Class 分析 ...

  9. Android LayoutInflater源码解析:你真的能正确使用吗?

    版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 好久没写博客了,最近忙着换工作,没时间写,工作刚定下来.稍后有时间会写一下换工作经历.接下来进入本篇主题,本来没想写LayoutInflater的 ...

  10. Android HandlerThread 源码分析

    HandlerThread 简介: 我们知道Thread线程是一次性消费品,当Thread线程执行完一个耗时的任务之后,线程就会被自动销毁了.如果此时我们又有一 个耗时任务需要执行,我们不得不重新创建 ...

最新文章

  1. ubuntu 14.04 mysql 5.7_ubuntu14.04 升级mysql到5.7版本
  2. Go 语言读写 Excel
  3. android冒烟测试自动化,自动化冒烟测试脚本应当遵循的原则
  4. 【论文解读】NLP重铸篇之Word2vec
  5. linux 挂载exfat u盘 yum,centos挂载exfat u盘
  6. 套接字编程--1(UDP协议编程,端口号,传输层协议,网络字节序)
  7. linux常用网络命令ping和arping
  8. CentOS 7 上搭建 ZooKeeper 集群
  9. c#---params参数
  10. js中的数组和字符串的一些方法
  11. 上传图片方法大全 [网摘]
  12. 在windows下把txt文件改为utf8格式
  13. html设计效果图,网页效果图如何制作?
  14. uc android快捷键,UC手机浏览器助力Android快速上网
  15. Python3简单的爬虫项目 爬取虎牙主播名字 人气
  16. 液滴模板水凝胶的温度控制形状变化(定制PNIPA/膨润土复合水凝胶/聚乙烯醇/魔芋胶复合凝胶/海藻酸钠(SA)/氧化石墨烯(GO)复合水凝胶/壳聚糖(CS)-g-PAM/氧化石墨烯(GO)复合水凝胶)
  17. 干货|科技赋能财富硬核直播带货,助力宜信财富逆势增长
  18. 计算两个日期之间所间隔天数的快速算法
  19. 老枪的59条制胜法则
  20. [K210]Maixpy self learning classifier 自学习分类器

热门文章

  1. 最新最全latex在sublime上的配置步骤全解
  2. 【dfs+简单贪心】Leaf Sets【Codeforces Round #510 (Div. 2)】
  3. 复旦nlp实验室 nlp-beginner 任务二:基于深度学习的文本分类
  4. 算法笔记-二分查找和二分答案
  5. linux三剑客之sed命令,linux三剑客之sed命令
  6. php 前后端 不对称加密,AES前后端对称加密
  7. 使用MVC2模式创建新闻网站
  8. NPM 上传自己的包
  9. [Hadoop]-Yarn-调度器篇
  10. Eclipse中设置自定义文档签名