1、简介
HandlerThread顾名思义,就是Thread与Handler相结合,其本质还是一个线程,只是在该线程中添加了该线程相关的Handler,实现了轻量级的异步类,具有以下特点:
- 普通线程类:与普通线程的使用类似,需要通过新建线程对象并调用start()开启线程;
- 内部嵌套了Handler:包含与该线程相绑定的Handler,方便线程之间的数据交互;
- 串行运行:其内部通过Looper实现任务的串行阻塞队列。
2、简单使用
2.1 作为线程使用
相比普通的线程,HandlerThread有一大优势就是在该类中可以创建工作Handler对象,在异步任务结束后,发送至工作区的Handler的handleMessage中,而如果普通的线程需要自己手动的维持Looper的prepare()和loop()方法。
//1、创建和执行MYHandlerThread 类,添加线程名称
MYHandlerThread myHandlerThread = new MYHandlerThread("xiaohan");
myHandlerThread.start();
class MYHandlerThread extends HandlerThread {
public MYHandlerThread(String name) {
super(name);
}
//2、重写onLooperPrepared()方法
@Override
protected void onLooperPrepared() {
super.onLooperPrepared();
//执行耗时任务
SystemClock.sleep(3 * 1000);
//3、任务结束,将结果发送至工作区的Handler中
Message message = subHandler.obtainMessage();
message.what = 101;
message.obj = "finsh";
subHandler.sendMessage(message);
}
//4、创建工作区中的Handler 接收异步任务的处理结果
Handler subHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
String result = (String) msg.obj;
Log.i(TAG, "handleMessage: 子线程中的Handler " + result);
}
};
}
//5、结束线程
mHandlerThread.quit();
2.2 作为“线程池”使用
//1、创建HandlerThread,添加线程名称
mHandlerThread = new HandlerThread("xiaohan");
//2、开启HandlerThread线程
mHandlerThread.start();
//3、创建WorkHandle工作区,处理不同的异步任务
mWorkHandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_THREAD1:
Log.i(TAG, "handleMessage: 子线程处理任务1");
break;
case UPDATE_THREAD2:
Log.i(TAG, "handleMessage: 子线程处理任务2");
break;
}
}
};
Message message = mWorkHandler.obtainMessage();
message.what = UPDATE_THREAD1;
message.obj = num + "";
mWorkHandler.sendMessage(message);
//4、结束线程
mHandlerThread.quit();
简要说明:
- 创建HandlerThread对象:创建HandlerThread对象,填写线程名称,通过start()启动该线程;
- 创建工作区Handler:通过getLooper()获取当前线程的Looper对象,在创建Handler时与其绑定,从而该Handler持有了Looper以及MessageQueue,正常工作。
- 发送异步任务:根据需要发送异步的Message 信息至工作区的handleMessage中,进行耗时操作。
3、源码分析
源码的分析,参考第二部分中使用的步骤,可以看出以上两种方式都大致分为5步。下面参考2.2中的执行流程分析如下:
3.1 创建HandlerThread,添加线程名称
HandlerThread的构造方法如下:
//线程优先级
int mPriority;
//线程号
int mTid = -1;
//Looper对象
Looper mLooper;
//Handler 对象
private @Nullable Handler mHandler;
//只包含线程名称的构造,其默认线程优先级为默认的:0
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
//包含线程名称和优先级的构造
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
本部分主要是新建HandlerThread类,设置进程名称和优先级。
3.2 开启HandlerThread线程
调用start()后开始执行线程的run方法,如下所示:
@Override
public void run() {
//获取线程号
mTid = Process.myTid();
//调用prepare()方法,准备MessageQueue
Looper.prepare();
//阻塞等待,查看Looper是否准备完成,其notifyAll()主要针对下面的getLooper()中的wait()方法
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
//设置线程优先级
Process.setThreadPriority(mPriority);
//自定义时,重写该方法,实现异步操作
onLooperPrepared();
//开启loop循环
Looper.loop();
mTid = -1;
}
- 针对开启线程后,主要完成Looper的准备、阻塞和开启;
- 提供自定义重写onLooperPrepared()方法调用;
- 开启loop,执行消息的存储和派发
3.3 创建WorkHandle工作区,处理不同的异步任务
mWorkHandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_THREAD1:
Log.i(TAG, "handleMessage: 子线程处理1");
break;
case UPDATE_THREAD2:
Log.i(TAG, "handleMessage: 子线程处理2");
break;
}
}
};
- 创建Handler:获取HandlerThread的Looper对象,将其与工作Handler相绑定;
- 重写handleMessage方法:由于WorkHandler与HandlerThread的线程相绑定,因此其handleMessage是运行在其子线程中;
熟悉Handler的都知道,在子线程中调用Handler时,必须要与Looper相绑定,否则无法获取MessageQueue和执行loop()方法,那如何保证在运行工作区Handler时Looper对象已经准备好?
解决方案如下:
@Override
public void run() {
...
//1、开始准备Loop
Looper.prepare();
//2、阻塞准备,加锁准备
synchronized (this) {
//3、Looper准备完成,取消阻塞等待,发送通知,释放锁
mLooper = Looper.myLooper();
notifyAll();
}
...
}
public Looper getLooper() {
...
synchronized (this) {
//4、等待Looper是否准备完成
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
...
//5、发送Looper对象
return mLooper;
}
主要流程如下:
- 准备Looper:通过Looper.prepare(),开始准备Looper对象;
- 阻塞等待:加锁(synchronized ),去准备Looper对象,准备完成后开始通知释放锁;
- 完成Looper的获取:在通过getLooper()时,一直加锁等待Looper的准备完成,直至 notifyAll()释放锁,因此在getLooper()时确保Looper准备完成。
完成工作区Handler后就可以根据需要发送消息至其handleMessage中,执行异步的任务处理
3.4 结束线程
调用HandlerThread的quit()方法,结束线程,查看源码可以发现其线程的结束包括两个方法:quit()和quitSafely(),其中:quit()线程不安全,quitSafely()是线程安全的。如果需要线程安全,其肯定会引起执行效率降低。
//HandlerThread
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;
}
对比两个方法可以看出,其执行线程结束主要是借助Looper 的quit()和quitSafely()方法,而Looper中时对MessageQueue的quit赋值true或者false控制。
//Looper
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
//MessageQueue
void quit(boolean safe) {
...
//判断是否为线程安全
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
...
}
//移除所有消息 线程不安全
//遍历所有消息,并置空
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
//移除所有消息 线程安全
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
if (p.when > now) {
removeAllMessagesLocked();
} else {
....
}
}
简要说明:
区分:从HandlerThread–Looper --MessageQueue可以看出,最终区分是否安全是对于MessageQueue中的quit方法,而查看其源码可以看出,线程安全的处理是在回收消息时,判断消息的时间,如果该消息未处理完成则等待处理,否则直接处理(与线程不安全类似)