Handler是非常频繁使用于各种通信的一种工具,HandlerThread则是比较少用,但是二者之间具有一定的联系,今天来整理一下他们的大概知识点。现在我们先看看Handlder,了解之后再学习HandlerThread。
目录大概如下:
1.Handler是什么
2.Handler的常规使用
3.Handler的机制
4.Handler的内存泄漏与解决方案
5.使用Handler让子线程与子线程相互通信
6.Handler的源码解析简要
7.HandlerThread是什么
8.HandlerThread的使用方法
9.HandlerThread的机制原理
从以上内容,介绍Handler与HandlerThread的基本知识。
1.Handler是什么
Handler是用于消息传送的一种异步机制工具,可以收放与处理Message、通过Runnable对象实现关联相关线程的MessageQueue。总的来说常用的功能有:
(1)可以让对应的Message、Runnable在某个时间点得到处理
(2)可以将耗时的操作放在子线程,然后发送接收消息,通知主线程进行UI更新
2.Handler的常规使用
Handler():默认构造函数将此处理程序与Looper用于当前线程(Looper.myLooper, 因此如果是子线程,则需要在其之前Looper.prepare,为当前线程新建looper,在其构造初始化完成后启动循环,Looper.loop,详见参考书籍二第212页)。
Handler(Handler.Callback callback):构造函数将此处理程序与Looper对于当前线程,并接受一个回调接口,您可以在其中处理消息。
Handler(Looper looper):使用所提供的Looper而不是默认的。
Handler(Looper looper, Handler.Callback callback):使用所提供的Looper而不是默认的,而是接受一个回调接口来处理消息。
接下来我们就来看看Handler提供的各种方法吧:
post(Runnable r):导致将Runnable r添加到消息队列中。
postAtTime(Runnable r, long uptimeMillis):使Runnable r添加到消息队列,并在uptimeMillis.
postDelayed(Runnable r, long delayMillis):使Runnable r被添加到消息队列,并在指定的时间流逝后运行。
removeCallbacks(Runnable r):删除消息队列中所有可运行的Runnable消息任务。
removeMessages(int what):删除消息队列中消息对象what字段为"what"的消息任务。
sendEmptyMessage(int what):发送一个空消息对象,并设置这个空消息的what值。
sendEmptyMessageAtTime(int what, long uptimeMillis):发送只包含要在特定时间传递的值的消息。
sendEmptyMessageDelayed(int what, long delayMillis):发送一条消息,该消息只包含要在指定的时间间隔后传递的值。
sendMessage(Message msg):将消息推送到消息队列的末尾,在当前时间之前完成所有挂起的消息。
sendMessageAtTime(Message msg, long uptimeMillis):在所有挂起的消息在绝对时间之前uptimeMillis(以毫秒为单位)之前,将消息放入消息队列中。
sendMessageDelayed(Message msg, long delayMillis):在所有挂起的消息之前(当前时间+delayMillis)之后,将消息放入消息队列中。
3.Handler的机制
Handler机制是一个异步消息机制,由Handler、Message、MessageQueue、Looper四部分所组成,下面我们着重介绍一下这四个的介绍:
(1)Handler:用于发送、接受、处理消息的处理者角色。详细地说就是用于在子线程发送消息对象Message,在UI线程处理消息对象Message,在子线程调用sendMessage方法发送消息对象Message,而发送的消息经过一系列地辗转之后最终会被传递到Handler的handleMessage方法中,最终在handleMessage方法中消息对象Message被处理;
(2)Message:消息,就是传递的数据封装对象,也可视为数据的载体;
(3)MessageQueue:消息的队列,用于存放Handler所发送过来的消息,这些消息则在此等待被处理。每个线程中只会有一个MessageQueue对象,请牢记这句话。其实从字面上就可以看出,MessageQueue底层数据结构是队列,而且这个队列只存放Message对象;
(4)Looper:MessageQueue的管家,也就是消息队列的管理者,因为每个线程只有一个消息队列,因此每个线程也只有一个looper。一般通过Looper.prepare进行原线程looper的新建初始化,在调用Looper.loop()后则开启循环,从MessageQueue获取消息Message,然后获取其对应的handler,回调handler.dispatchMessage,进行消息传递与唤起消息处理(Handler对象的handleMessage方法),直到MessageQueue里的消息被全部取出跳出loop里面的循环;
在这里补充一下,因为每个线程只能有一个Looper,也因此只能有一个MessageQueue。但是为什么每个线程只能有一个Looper呢?因为在源码里面,是通过静态对象sThreadLocal进行Looper的存储与集合管理(或者说用来保存Looper 对象),就相当于一个Map集合,键位当前的Thead线程,值为Looper对象。源码如下:
public final class Looper{
private static final String TAG = "Looper";
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;
private Printer mLogging;
...
}
从以上四个部分的角色介绍,我们再描述一下Handler的最常用场景进行描述一下:在UI线程进行handler的初始化,无论是匿名内部类还是其对应静态内部类、子类等都需要重写handleMessage方法,作为消息回调处理,通过参数msg来写接受消息过后UIi线程的逻辑处理的场所。之后,我们获取主线程的handler对象,传递给我们的子线程,在子线程内部进行数据获取/处理等耗时操作回调,将回应数据进行封装成msg,handler.sendMessage进行msg发送数据。至此,这是上层应用开发者所必须知晓的流程,接下来的步骤就是内部机制实现了;
在sendMessage调用之后,msg则存放到对应looper的消息队列messageQueue中,等待被处理。在loop 启用后,则进行对消息队列循环获取消息msg,然后获取对应msg对应的handler,通过回调handler的dispatchMessage方法(分发msg)将消息传递给Handler的handleMessage方法。
自此,整个handler的机制大致完成,可见下图:
4.handler的内存泄漏
原因:非静态内部类持有外部类的匿名引用,导致外部activity无法得到释放。
解决方法:handler内部持有外部的弱引用,并把handler改为静态内部类,在activity的onDestory()中调用handler的removeCallback()方法。
5.使用Handler让子线程与子线程相互通信
在handler初始化,之前进行该线程looper的新建与初始化Looper.prepare,之后进行调用loop。详细见参考博文
6.Handler的源码解析简要
https://blog.csdn.net/u012827296/article/details/51236614
7.HandlerThread是什么
先说一下需求,由于多次新建和关闭子线程都十分耗费资源,因此推出HandlerThread机制,来对应这类场景。本质上来说,Handler + Thread + Looper,是一个Thread内部有Looper,其继承于Thread。细化来看就是:
(1)HandlerThread是继承于Thread,是一个线程类;
(2)HandlerThread内部具有Looper对象,可以在自己内部进行loop循环;
(3)通过Looper对象将消息传递给内部的Handler对象,可在handleMessage方法里面执行异步任务;
(4)它的优点——不会阻塞,对性能消耗比较小;缺点——不能进行多任务的执行,需要等待处理,效率较低;
(5)与多线程并发机制不同,它是(队列)串行方式,HandlerThread只有一个线程。
8.HandlerThread的使用方法
HandlerThread最明显的就是支持了主线程对子线程的通信。其完整的通信过程应该是:主线程通知子线程,再由子线程通知主线程。对比来看传统的方式就是:在任务明确后进行子线程创建,然后子线程将结果通知给主线程。这样的好处就是,主线程可以告知子线程需要干什么,而且可以随时随地地进行消息通知,子线程在处理完对应任务后再通知主线程。当然,这样就不可避免了,你主线程本身还是需要一个Handler进行消息回调而更新UI。
详细样例可见参考博文。
9.HandlerThread的机制原理
HandlerThread在内部拥有一个Looper,并在run方法中进行创建,然后放入ThreadLocal里面,在调用loop方法唤起循环。Looper.loop()方法会不断循环从MessageQueue中取出消息处理消息,没有消息是则会阻塞。getLooper()方法是获取线程中的Looper对象,可以用来初始化Handler对象。
这里说一下 HandlerThread类中的quit、quitSafely方法区别,quit会清空队列里的消息(无论延时或者非延时),而quitSafely则只清空消息队列里的延时消息。当调用qiut或者quitSafely,Looper对象都不会再接收消息了,也就是说就停止消息的循环了。这时候再调用sendMessage或者post,都会返回false,线程也随即停止。
---------------------
参考书籍:《Android开发艺术探索》、《疯狂Android讲义(第3版)》
参考博文:
作者:ClAndEllen 来源:CSDN 题目:Android面试系列文章2018之Android部分HandlerThread机制篇
链接:https://blog.csdn.net/ClAndEllen/article/details/79346492
---------------------