0、目录

一、HandlerThread简介

二、HandlerThread原理

三、HandlerThread使用实例

四、HandlerThread源码分析

五、总结

一、HandlerThread简介

HandlerThread是一个轻量级的异步类,可以实现多线程,并且可以实现线程间的通信(HandlerThread主要应用是实现主线程到子线程的通信,子线程到主线程通信可以通过Handler机制)。

二、HandlerThread原理

既然已经有Handler可以实现线程间通信,为什么又设计了HandlerThread?

HandlerThread通过字面意思我们可以看到,它是Handler+Thread,那么我们猜测它应该实现了Handler和Thread功能,到底是不是呢,我们向下看。

首先,HandlerThread继承了Thread类,也就是说HandlerThread可以创建一个新的线程。

其次,HandlerThread内封装了Handler类,并自动创建了Looper和MessageQueue,也就是说我们使用HandlerThread线程时,不需要手动创建Looper。

优点:

之前我们使用Handler时,必须自己创建线程,并且自己创建Looper等相关的功能,而HandlerThread则提供了一种方便的方式,相当于内部已经集成了这些功能,不需要我们再手动创建Looper。

三、HandlerThread使用实例

使用步骤:

使用步骤:
//1、创建HandlerThread对象,即创建了一个新的线程,参数为线程名,仅仅是标记线程用的HandlerThread mHandlerThread = new HandlerThread("mHandlerThread");
//2、开启线程,第一步创建了一个新的线程,此处开启线程。mHandlerThread.start();
//3、创建Handler,并重写handleMessage()方法//new Handler(mHandlerThread.getLooper()),即把该Handler绑定到mHandlerThread线程的Looper,//进而绑定到了线程mHandlerThreadHandler mHandlerInHandlerThread = new Handler(mHandlerThread.getLooper()){@Overridepublic void handleMessage(Message msg) {//对信息的相关处理操作               //在子线程mHandlerThread中运行   super.handleMessage(msg);}};
//4、创建消息并发送消息//在主线程中Message msg = Message.obtain();//主线程向子线程mHandlerThread发送消息通信mHandlerInHandlerThread.sendMessage(msg);
//5、结束线程。之前开启线程,当工作结束不再使用该线程时,应该结束该线程//即停止了线程的消息循环mHandlerThread.quit();

通过使用步骤我们可以看到,HandlerThread实现的功能主要就是主线程向子线程通信,另外可以在使用Handler实现子线程到主线程的通信,进而就可以实现主线程到子线程间的双向通信.

使用实例:

使用实例:
//功能介绍:点击按钮实现延时操作,延时时间到后更新UI。
public class MainActivity extends AppCompatActivity {TextView mTxtShowTest;Button  mBtnInnerClass ,mBtnHandlerThread, mBtnQuit;//1、创建HandlerThread对象,即创建了一个新的线程,参数为线程名,仅仅是标记线程用的HandlerThread mHandlerThread = new HandlerThread("mHandlerThread");//匿名内部类,用于子线程向主线程通信用private Handler mhandlerInnerClass =  new Handler(){@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);switch (msg.what){case 1://执行的UI操作mTxtShowTest.setText("点击了mBtnInnerClass");break;case 2://执行的UI操作mTxtShowTest.setText("来自于mHandlerInHandlerThread的请求更新");break;}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mTxtShowTest = (TextView) findViewById(R.id.mTxtShowTest); mBtnInnerClass = (Button) findViewById(R.id.mBtnInnerClass); mBtnHandlerThread = (Button) findViewById(R.id.mBtnHandlerThread);mBtnQuit = (Button) findViewById(R.id.mBtnQuit);//2、开启线程,第一步创建了一个新的线程,此处开启线程。mHandlerThread.start();//3、创建Handler,并重写handleMessage()方法//new Handler(mHandlerThread.getLooper()),即把该Handler绑定//到了mHandlerThread线程的Looper,进而绑定到了线程mHandlerThreadfinal Handler mHandlerInHandlerThread = new Handler(mHandlerThread.getLooper()){@Overridepublic void handleMessage(Message msg) {//已经是在子线程mHandlerThread中运行了//可以进行一些耗时等操作try{Thread.sleep(10000);  //延时操作}catch (Exception e){e.getMessage();}//子线程向主线程通信Message msg1 = Message.obtain(); msg1.what = 2;mhandlerInnerClass.sendMessage(msg1);super.handleMessage(msg);}};mBtnInnerClass.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {new Thread(new Runnable() {@Overridepublic void run() { Message msg = Message.obtain(); msg.what = 1;mhandlerInnerClass.sendMessage(msg);} }).start();}});mBtnHandlerThread.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//4、创建消息并发送消息//主线程中Message msg = Message.obtain();//主线程向子线程发送信息,通信mHandlerInHandlerThread.sendMessage(msg);}});mBtnQuit.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//5、结束线程。之前开启线程,当工作结束不再使用该线程时,应该结束该线程//即停止了线程的消息循环mHandlerThread.quit();}});}
}

注意:

当连续多次点击按钮mBtnHandlerThread是,mTxtShowTest中显示,并不是同时显示的,而是先显示第一次,间隔延时10s(这个10s是在复写handleMessage()方法时自己写的 Thread.sleep(10000))后显示第二次,再间隔延时的10s显示第三次。当我们连续多次点击mBtnHandlerThread按钮时,消息入队列,取出消息执行时,有一个延时10s,之后更新UI,这个消息才算执行完成,然后从消息队列中取出下一个消息,同样有一个延时10s,之后才是更新UI,所以即便时快速连续多次点击按钮,但是执行时每次都间隔一个延时10s.

总结:

以上就是HandlerThread的使用步骤和使用实例,已经测试过可以使用。首先点击按钮mBtnInnerClass, mTxtShowTest显示"点击了mBtnInnerClass",之后再点击按钮mBtnHandlerThread,十秒之后,mTxtShowTest显示"来自于mHandlerInHandlerThread的请求更新"。多次点击按钮mBtnHandlerThread,然后点击mBtnInnerClass,mTxtShowTest显示"点击了mBtnInnerClass",10s后mTxtShowTest显示"来自于mHandlerInHandlerThread的请求更新",之后再点击mBtnInnerClass,mTxtShowTest显示"点击了mBtnInnerClass",10s后又会mTxtShowTest显示"来自于mHandlerInHandlerThread的请求更新",即可证明前面注意中说明的问题。

四、HandlerThread源码分析

在第三部分使用实例中的使用步骤可以看到,使用HandlerThread大概需要5步,具体如下:

使用步骤:
//1、创建HandlerThread对象,即创建了一个新的线程,参数为线程名,仅仅是标记线程用的HandlerThread mHandlerThread = new HandlerThread("mHandlerThread");
//2、开启线程,第一步创建了一个新的线程,此处开启线程。mHandlerThread.start();
//3、创建Handler,并重写handleMessage()方法//new Handler(mHandlerThread.getLooper()),即把该Handler绑定到mHandlerThread线程的Looper,//进而绑定到了线程mHandlerThreadHandler mHandlerInHandlerThread = new Handler(mHandlerThread.getLooper()){@Overridepublic void handleMessage(Message msg) {//对信息的相关处理操作               //在子线程mHandlerThread中运行   super.handleMessage(msg);}};
//4、创建消息并发送消息//在主线程中Message msg = Message.obtain();//主线程向子线程mHandlerThread发送消息通信mHandlerInHandlerThread.sendMessage(msg);
//5、结束线程。之前开启线程,当工作结束不再使用该线程时,应该结束该线程//即停止了线程的消息循环mHandlerThread.quit();

我们根据使用步骤 ,一步一步的去看源码。

步骤1、创建HandlerThread对象

源码分析:HandlerThread mHandlerThread = new HandlerThread("mHandlerThread");
/*** Handy class for starting a new thread that has a looper. The looper can then be * used to create handler classes. Note that start() must still be called.*/
//通过HandlerThread 的定义,其继承Thread ,可知HandlerThread 就是个线程
public class HandlerThread extends Thread {int mPriority;//优先级int mTid = -1;Looper mLooper;//当前线程的Looperprivate @Nullable Handler mHandler;public HandlerThread(String name) {super(name);mPriority = Process.THREAD_PRIORITY_DEFAULT;}public HandlerThread(String name, int priority) {super(name);mPriority = priority;}...
}

总结:

HandlerThread就是个线程,继承了Thread。创建HandlerThread 时,就相当于创建了一个新的线程,并设置了该线程的优先级。

步骤二、开启线程 mHandlerThread.start();

源码分析:
//Causes this thread to begin execution; the Java Virtual Machine
//calls the run method of this thread.
public synchronized void start() {...}//调用mHandlerThread的start(),最终将调用mHandlerThread的run()方法。@Overridepublic void run() {mTid = Process.myTid();//获取当前线程的IDLooper.prepare();//创建Looper和MessageQueue//通过锁机制,获取当前线程的Looper对象synchronized (this) {//获取当前线程的Looper对象mLooper = Looper.myLooper();//发送通知,已经获取当前线程的Looper对象//主要是在第三步创建Handler中的mHandlerThread.getLooper()内使用notifyAll();}Process.setThreadPriority(mPriority);//设置线程的优先级//主要是做一些开启消息循环前的准备工作 -->>分析1onLooperPrepared();//开启消息循环Looper.loop();mTid = -1;}
分析1: onLooperPrepared()/*** Call back method that can be explicitly overridden if needed to execute some* setup before Looper loops.*/protected void onLooperPrepared() {}

总结:

第一步创建了线程,第二步开启该线程。开启线程时,会创建Looper和MessageQueue,成功后线程会进入消息循环,不断从消息队列中取出消息并分发消息。

步骤三、创建Handler,并重写handleMessage()方法

使用方法:
Handler mHandlerInHandlerThread = new Handler(mHandlerThread.getLooper()){@Overridepublic void handleMessage(Message msg) {//对信息的相关处理操作               //在子线程mHandlerThread中运行   super.handleMessage(msg);}};//创建Handler时传入Looper对象,此时Handler就与该Looper绑定,进而与Looper所在的线程也绑定
//获取当前线程(即mHandlerThread线程)的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 {//等待,直到mHandlerThread的run()方法创建了Looper//并 notifyAll()发送通知wait();} catch (InterruptedException e) {}}}return mLooper;}

总结:

复写handleMessage()方法,该方法是在子线程mHandlerThread中运行 ,而非主线程,要注意。因为是在子线程中,所以可以进行一些耗时相关的操作。另外,通过Handler机制,可以实现该子线程与主线程的通信,进而更新UI。

步骤四、创建消息并发送消息

 //在主线程中Message msg = Message.obtain();//主线程向子线程mHandlerThread发送消息通信mHandlerInHandlerThread.sendMessage(msg);//与Handler中的使用方法一样,就不多做介绍了。

步骤五、结束线程

//之前开启线程,当工作结束不再使用该线程时,应该结束该线程
//即停止了线程的消息循环
//源码分析:mHandlerThread.quit();public boolean quit() {//获取当前LooperLooper looper = getLooper();if (looper != null) {//调用Looper的quit()方法 -->>分析1looper.quit();return true;}return false;}//分析1: looper.quit();public void quit() {//调用MessageQueue的quit()方法 -->>分析2mQueue.quit(false);}
//通过looper.quit()退出是一种不安全的退出方法,
//还有一种安全的退出方法,即looper.quitSafely();public boolean quitSafely() {Looper looper = getLooper();if (looper != null) {looper.quitSafely();//-->>分析areturn true;}return false;}
//分析a looper.quitSafely()的源码实现:public void quitSafely() {//通过源码,截至到目前可以看出,安全不安全主要是mQueue.quit()方法//内的boolean变量是true还是false mQueue.quit(true);// -->>分析2}分析2: mQueue.quit(false);void quit(boolean safe) { synchronized (this) {if (mQuitting) {return;}mQuitting = true;if (safe) {//安全的退出方式removeAllFutureMessagesLocked();// -->>分析4(请先看分析3)} else {//非安全的退出方式removeAllMessagesLocked();// -->>分析3}// We can assume mPtr != 0 because mQuitting was previously false.nativeWake(mPtr);}}//分析3、 removeAllMessagesLocked()//不管该消息是否在使用,把消息队列中的所有消息都回收private void removeAllMessagesLocked() {Message p = mMessages;while (p != null) {Message n = p.next;//Recycles a Message that may be in-use.//这里,不管Message是否在使用,都回收销毁,//所以也就决定了它肯定是不安全的p.recycleUnchecked();p = n;}mMessages = null;}//分析4、 removeAllFutureMessagesLocked()
//首先判断是否有消息再使用中,如果没有就按照分析3的方法全部回收,
//如果有消息正在使用中,这个使用的消息不做回收,它依然能够正常执行完成,
//而其他的所有的没有正在执行的全部回收private void removeAllFutureMessagesLocked() {//Returns milliseconds since boot, not counting time spent in deep sleep.final long now = SystemClock.uptimeMillis();Message p = mMessages;if (p != null) {if (p.when > now) {//判断消息队列现在是否正在处理消息,如果没有则直接把所有消息回收//通过时间来判定的removeAllMessagesLocked();} else {//有正在处理的消息,则处理完成后再退出Message n;for (;;) {n = p.next;if (n == null) {return;}if (n.when > now) {break;}p = n;}p.next = null;do {p = n;n = p.next;p.recycleUnchecked();} while (n != null);}}}

总结:

所谓的安全与否,关键是对正在使用中的消息如何处理来判断的,如果直接回收消息就是不安全,等待它处理完成就是安全的。

五、总结

本文对HandlerThread做了全部分析,从原理、使用实例,到源码分析,希望对您有帮助。

HandlerThread原理、使用实例、源码详细解析相关推荐

  1. spark word2vec 源码详细解析

    spark word2vec 源码详细解析 简单介绍spark word2vec skip-gram 层次softmax版本的源码解析 word2vec 的原理 只需要看层次哈弗曼树skip-gram ...

  2. ArrayList源码详细解析(一)

    Java ArrayList源码解析(基于JDK 12,对比JDK 8) 自从接触到ArrayList以来,一直觉得很方便,但是从来没有系统.全面的学习了解过ArraryList的实现原理.最近学习了 ...

  3. 20行Python代码爬取2W多条音频文件素材【内附源码+详细解析】新媒体创作必备

    大家好,我是辣条. 今天的内容稍显简单,不过对于新媒体创作的朋友们还是很有帮助的,你能用上的话记得给辣条三连! 爬取目标 网站:站长素材 工具使用 开发工具:pycharm 开发环境:python3. ...

  4. JAVA8 LinkedList 链表源码详细解析

    今天突发奇想看了一下LinkedList的源码,还挺有趣的,话不多说,show me the code. 我使用的是IDEA,快捷键仅供参考. 按住Ctrl再点击类名可以进入类的源码,随便写一个含有L ...

  5. Hadoop HDFS创建文件/写数据流程、源码详细解析

    HDFS创建文件/写数据源码解析 HDFS HDFS写流程 创建文件源码 客户端 DistributedFileSystem DFSClient DFSOutputStream 客户端/Namenod ...

  6. golang mutex源码详细解析

    目前golang的版本是1.12,其中的mutex是增加了普通模式和饥饿模式切换的优化版本,为了便于理解,这里先从上一个版本1.7版本的mutex开始分析,以后再对优化版本进行说明. Mutex结构说 ...

  7. Faster_R_CNN源码详细解析

    Faster R-CNN整体架构 首先使用共享卷积层为全图提取特征feature maps 将得到的feature maps送入RPN,RPN会产生接近两千个候选框proposals RoI Pool ...

  8. 火车轨道铁路轨道检测识别(附带Python源码+详细解析)

    现在的网络上,铁轨检测的源码几乎没有,所以自己参照着一篇汽车车道线检测的方法,然后调节参数,实现了铁轨的轨道检测,但现在只能检测直线,弯曲的铁轨检测下一步会实现,实现之后会更新的,敬请期待. 弯轨检测 ...

  9. MJRefresh 源码详细解析

    MJRefresh是李明杰老师的作品,到现在已经有9800多颗star了,是一个简单实用,功能强大的iOS下拉刷新(也支持上拉加载更多)控件.它的可定制性很高,几乎可以满足大部分下拉刷新的设计需求,值 ...

最新文章

  1. java反射,代码优化
  2. 如何通过DBLINK取REMOTE DB的DDL
  3. 网易邮箱大师如何屏蔽邮件 屏蔽垃圾邮件的方法步骤
  4. php rgb,php颜色转换函数hex-rgb
  5. Java集合Set、Map、HashSet、HashMap、TreeSet、TreeMap等
  6. sql出现无法启动(model数据库)
  7. NTC热敏电阻温度计算以及C语言实现
  8. 免费下载中国知网、万方学术论文的几种方法(福利合集)
  9. 重装服务器系统只装C盘,如何只重装C盘的系统?
  10. 【FFmpeg】【转载】图像拼接:画中画连麦
  11. 一小段经历后的感想和自述。
  12. Docker环境下的前后端分离部署与运维 脚本
  13. 从小白到web渗透工程师——零基础指南(1)web渗透工程师介绍
  14. cesium里面能不能导入动画_cesium 添加动态 gif 图片
  15. 找到Tiny OS的创始人,畅谈新时代的爆款“物联网”
  16. DevOps教程:DevOps 工具
  17. 阿里云第七代ECS现已上线
  18. linux分区Error creating partition
  19. Nginx 最大连接数配置
  20. InvocationHandle的invoke方法

热门文章

  1. 调用socket接口
  2. mathtype花写字母
  3. 基于边缘计算盒子CBD大厦智慧视频分析入门方案
  4. 使用python3.7.2 实现大名鼎鼎的Elo Score等级分制度
  5. k8s教程(pod篇)-配置管理
  6. C# 使用委托实现异步编程的四种方式
  7. 奈奎斯特准则的简洁证明
  8. 如何加入政府采购网成为供应商?
  9. APIO 2018 游记
  10. zigbee配置及常见错误总结---(Segment BANKED_CODE must be defined in a segment definition option)