文章目录

  • Thread基本用法
  • HandlerThread基本用法
  • HandlerThread和Handler结合使用
  • 用例演示
  • 总结

相信很多同学都听说并使用过HandlerThread、Thread、Handler,但具体对这三种的用法和区别你真有认真总结过吗?
本文帮你区别并了解三者的基本用法。

Thread基本用法

Thread类实现了Runnable接口,必须重写run()方法;

//匿名内部类形式
new Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stub}
});

或者

class myThread implements Runnable {@Overridepublic void run() {// TODO Auto-generated method stub}
}

启动一个线程,用start()方法;

HandlerThread基本用法

看源码开头的方法注释,我们可以很清楚的了解这个类的用途:

/*
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可以方便的开启一个线程,并且这个线程已经绑定了一个looper了。这个looper可以用来new一个handler类。注意在此之前,要调用start()方法。

示例代码如下:

HandlerThread thread1 = new HandlerThread("test1");
thread1.start();

看到这里很多同学可能会产生一个疑问,为什么这个线程已经绑定了一个Looper了呢?还要在此之前调用start()方法?

答案就是:提前调用start()方法就是为了为线程绑定Looper的。
我们知道,start()方法可以开启一个线程,调用线程的run()方法。而HandlerThread的run()方法是这样的:

@Override
public void run() {mTid = Process.myTid();Looper.prepare();// HandlerThread在start()的时候执行run()方法,而Looper就是在这里被创建的synchronized (this) {mLooper = Looper.myLooper();notifyAll();}Process.setThreadPriority(mPriority);onLooperPrepared();Looper.loop();mTid = -1;
}

看到没,run()方法里面调用Looper.prepare()来为线程绑定一个Looper。 Looper.loop()开启消息循环。相当于把原来的需要调用Looper.prepare()、Looper.loop()做了一下封装。

有了Looper之后,我们就可以创建Handler,就可以在这个线程里面发送消息、处理消息了。

HandlerThread和Handler结合使用

我们知道,Handler在使用时,需要一个Looper(以及Looper的消息队列MessageQueue),这样它才能发送消息,处理消息。

根据Handler创建方法的不同,我们可以这样用:

HandlerThread thread1 = new HandlerThread("test1");
thread1.start();
Handler mHandler = new Handler(thread1.getLooper()); // Handler创建方法1

// 或者: Handler创建方法2

Handler mHandler = new Handler(thread1.getLooper(), new Callback() {@Overridepublic boolean handleMessage(Message msg) {// TODO Auto-generated method stubreturn false;}
});

// 或者 Handler创建方法3

Handler.Callback callback = new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {// TODO Auto-generated method stubreturn false;}
};Handler mHandler = new Handler(thread1.getLooper(), callback);

// Handler创建方法4,重写HandleMessage方法

static class TestHandler extends Handler {public TestHandler(Looper looper) {super(looper);}@Overridepublic void handleMessage(Message msg) {}
}

HandlerThread 终止消息循环的方法

使用完HandlerThread时,不要忘了退出消息循环。

HandlerThread终止消息循环有两种方法,quit()和quitSafely(),调用的是Looper的对用方法,而Looper调用的是MessageQueue的对应方法。

quitSafely()源码:

public boolean quitSafely() {Looper looper = getLooper();if (looper != null) {looper.quitSafely();return true;}return false;
}

发现HandlerThread的quitSafely()其实是调用了Looper的quitSafely()方法:

public void quitSafely() {mQueue.quit(true);
}

同样,HandlerThread的quit()则是调用了Looper的quit()方法:

public void quit() {mQueue.quit(false);
}
用例演示
  1. 首先是DownloadThread类,继承于HandlerThread,用于下载。
public class DownloadThread extends HandlerThread{private static final String TAG = "DownloadThread";public static final int TYPE_START = 2;//通知主线程任务开始public static final int TYPE_FINISHED = 3;//通知主线程任务结束private Handler mUIHandler;//主线程的Handlerpublic DownloadThread(String name) {super(name);}/** 执行初始化任务* */@Overrideprotected void onLooperPrepared() {Log.e(TAG, "onLooperPrepared: 1.Download线程开始准备");super.onLooperPrepared();}//注入主线程Handlerpublic void setUIHandler(Handler UIhandler) {mUIHandler = UIhandler;Log.e(TAG, "setUIHandler: 2.主线程的handler传入到Download线程");}//Download线程开始下载public void startDownload() {Log.e(TAG, "startDownload: 3.通知主线程,此时Download线程开始下载");mUIHandler.sendEmptyMessage(TYPE_START);//模拟下载Log.e(TAG, "startDownload: 5.Download线程下载中...");SystemClock.sleep(2000);Log.e(TAG, "startDownload: 6.通知主线程,此时Download线程下载完成");mUIHandler.sendEmptyMessage(TYPE_FINISHED);}
}
  1. MainActivity部分,接收HandlerThread消息更新UI界面
public class MainActivity extends AppCompatActivity {private static final String TAG = "MainActivity";private DownloadThread mHandlerThread;//子线程private Handler mUIhandler;//主线程的Handler@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//初始化,参数为线程的名字mHandlerThread = new DownloadThread("mHandlerThread");//调用start方法启动线程mHandlerThread.start();//初始化Handler,传递mHandlerThread内部的一个loopermUIhandler = new Handler(mHandlerThread.getLooper()) {@Overridepublic void handleMessage(Message msg) {//判断mHandlerThread里传来的msg,根据msg进行主页面的UI更改switch (msg.what) {case DownloadThread.TYPE_START://不是在这里更改UI哦,只是说在这个时间,你可以去做更改UI这件事情,改UI还是得在主线程。Log.e(TAG, "4.主线程知道Download线程开始下载了...这时候可以更改主界面UI");break;case DownloadThread.TYPE_FINISHED:Log.e(TAG, "7.主线程知道Download线程下载完成了...这时候可以更改主界面UI,收工");break;default:break;}super.handleMessage(msg);}};//子线程注入主线程的mUIhandler,可以在子线程执行任务的时候,随时发送消息回来主线程mHandlerThread.setUIHandler(mUIhandler);//子线程开始下载mHandlerThread.startDownload();}@Overrideprotected void onDestroy() {//有2种退出方式mHandlerThread.quit();//mHandlerThread.quitSafely(); 需要API>=18super.onDestroy();}
}
  1. 运行结果log如下
总结

HandlerThread继承自Thread,因此HandlerThread其实就是一个线程。
线程开启时也就是run方法运行起来后,线程同时创建一个含有消息队列的looper,并对外提供自己这个对象的get方法,这就是和普通的Thread不一样的地方。可用于多个耗时任务要串行执行。比如涉及到异步任务执行并且要发消息给主线程更新UI,这时候就可以使用HandlerThread,当然,你也可以使用普通的Thread子线程去实现,但免不了要自己创建Looper、MessageQueue等消息机制的对象,既然Android有封装好的提供给我们,那又何必多此一举

通过上面的了解,很明显的可以看出,handlerThread在一个子线程里面封装好了looper和handler,所以这就是为什么在子线程里面使用handlerThread直接run就可以了。

在getLooper里面,handlerThread对线程是否存在加了一个判断,因为handler在主线程,而handlerThread创建的Looper在子线程,如果子线程没有创建或者子线程的Looper没有创建,那handler也不知道给谁发消息。所以这里handlerThread加了一层判断,确保子线程的Looper创建。

关于handlerThread的使用场景,一般就是在子线程中使用,因为如果按照正常的handler使用的话,需要自己额外在子线程新建一个Looper(由于主线程已经默认有一个Looper了,所以在主线程可以直接用handler)。例如在子线程不断地获取数据更新UI的时候,就可以用到handlerThread。

HandlerThread的使用场景和用法相关推荐

  1. C#-深入理解async和await的作用及各种适用场景和用法

    第十五节:深入理解async和await的作用及各种适用场景和用法 一. 同步VS异步 1.   同步 VS 异步 VS 多线程 同步方法:调用时需要等待返回结果,才可以继续往下执行业务 异步方法:调 ...

  2. 面试必问的一个知识点,关于HandlerThread的使用场景以及怎样使用 HandlerThread?

    前言 有些同学老问我,快要校招了 今年的金三银四退成了金四银五了,我改准备哪些知识点去面试复习,然而要准备的知识点很庞大,今天要讲的知识其中之一.一篇关于HandlerThread的解析 文档笔记~ ...

  3. HashTable,Dictionary,ConcurrentDictionary 的应用场景,区别,用法统计

    https://www.cnblogs.com/yinrq/p/5584885.html 一.HashTable HashTable表示键/值对的集合.在.NET Framework中,Hashtab ...

  4. Dubbo场景场景配置用法详解

    前言 对Dubbo常用的场景详解以及配置说明. dubbo配置来源有 JVM参数.外部配置.api配置(xml配置,注解配置).属性文件配置.优先级从高到低,这点是前提. 启动时检查 Dubbo会在启 ...

  5. reduce()使用场景、用法总结

    先来看MDN上的解释: reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值. 举个栗子: const array1 = [1, 2, 3 ...

  6. 第十五节:深入理解async和await的作用及各种适用场景和用法

    一. 同步VS异步 1.   同步 VS 异步 VS 多线程 同步方法:调用时需要等待返回结果,才可以继续往下执行业务 异步方法:调用时无须等待返回结果,可以继续往下执行业务 开启新线程:在主线程之外 ...

  7. 含泪讲述拿到美团offer的心酸历程......

    本人双非毕业,非科班出身,做安卓开发,在小公司打杂三年后,意外地拿到了美团的offer,成功跳槽到了美团外卖事业部. 我这几年经历了什么? 3年前,我末流本科毕业,进了编程培训班,从培训班出来之后,我 ...

  8. 渣硕Android开发找工作都这么难了吗?千万不要轻易离职......

    坐标北京,21年3月毕业工作,北京某大型互联网码农集散基地渣硕背景. 第一份工作在北京的一个80人左右规模的小公司做Android,最近刚刚跳槽成功. 做Android是从19年中旬开始,毕业前的第一 ...

  9. Java面试突击手册(2022版)每日一刷,金三银四稳了

    前言 春节一过金三银四就要开始了,我相信将会是面试求职的高峰时期,如果此时手里有份高质量的面试宝典,那么你将得心应手面对考官各种问题.虽然不敢保证你能应聘上心仪的职位,但是能保证看完这些内容你的收获将 ...

  10. Android 白学了。。。

    做Android开发这么久了,以下这些Android大厂常见面试题.你都掌握了吗? Android 四大组件相关 Activity 与 Fragment 之间常见的几种通信方式? LaunchMode ...

最新文章

  1. AI芯片的“战国时代”:计算力将会驶向何方?
  2. Python语言的程序结构
  3. 史密斯圆图串并联口诀_看得懂的史密斯圆图(个人总结)
  4. linux网络编程之怎么配置好unp.h文件
  5. 【MFC系列-第17天】企业信息管理软件开发
  6. 如何使用动态工具提示构建React Native图表
  7. ASP.NET AJAX Advance Tips Tricks (11) 三种方法动态创建Tooltip
  8. Flink 新一代流计算和容错——阶段总结和展望
  9. python绘制风向玫瑰图和污染物玫瑰图
  10. Nordic--nrf52832--FDS(二)基本使用
  11. 字节跳动Java工资待遇等级_字节跳动面试题:你的平均薪水是多少?
  12. linux命令离线手册下载,linux 命令速查手册全集下载
  13. uni-app 退出app操作
  14. SAP UI5 Cross Application Navigation (跨应用间跳转)的本地模拟实现试读版
  15. docker安装mysql遇到的问题
  16. iOS开发接入腾讯云通信简略笔记
  17. Oracle 按日期分组
  18. 冰丝凉席好不好 冰丝凉席怎样 冰丝凉席简介
  19. 基于协同过滤算法的电影推荐系统设计(二) - ALS算法详解
  20. 怎么将微博图片中的水印去掉

热门文章

  1. 联想服务器硬盘启动设置方法,教你联想台式机bios设定硬盘启动方法
  2. 开源爱好者李涛:三人行必有我师
  3. PC浏览器播放m3u8
  4. android listview viewgroup,Android View及ViewGroup事件分发机制总结
  5. 2021世界人工智能大会(WAIC2021):深思考人工智能获颁工信部人工智能产业创新“揭榜优胜单位”!
  6. UnityECS-初识
  7. oracle连续周数,oracle周数计算
  8. C++基础病毒性函数汇总
  9. 深度Deepin操作系统安装Docker
  10. 企业级网络突然变得很卡解决办法