不达成功誓不休 — 程端礼
写在前面
“送走四月,喜迎五月”,今年的五一小长假简直舒服到了极致,牺牲两个周六换来的四天假期。是时候出去浪了,打开微博发现不妙,吓得我赶紧躺床上又睡了一觉...
入职后经常加班,仅有的私人时间也没有心思学习。不过我还是告诉自己在忙也不能忘了充实自己。趁着今天有兴致,阅读了一下HandlerThread的源码。
用法
首先,我们来了解一下HandlerThread。
Q:什么是HandlerThread?
A:HandlerThread继承自Thread,可以说其是一个线程。并且有一个Looper对象进行消息循环。
接下来,我们看一下HandlerThread是如何执行异步任务的。
// 创建HandlerThread对象,参数为该线程的Name
HandlerThread handlerThread = new HandlerThread("work");
// 调用HandlerThread的start()函数开启线程
handlerThread.start();
// 通过调用HandlerThread的getLooper()函数得到Looper对象,并创建Handler用于执行异步任务
Handler workHandler = new Handler(handlerThread.getLooper(), new WorkHandlerCallback());
// 在主线程中创建Handler,用于更新UI(主线程中有一个Looper)
final Handler uiHandler = new Handler(new UIHandlerCallback());
// 发送一个异步任务的消息
Message msg = workHandler.obtainMessage();
msg.what = 0x01;
workHandler.sendMessage(msg);
private class WorkHandlerCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case 0x01:
// 开始执行异步任务
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
// 异步任务执行成功后,告知UI线程更新
Message message = uiHandler.obtainMessage();
message.what = 0x02;
message.obj = sum + "";
uiHandler.sendMessage(message);
break;
default:
break;
}
return true;
}
}
private class UIHandlerCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case 0x02:
// 更新UI
textView.setText((String) msg.obj);
// 异步任务做完以后,不再需要HandlerThread,就调用quit()函数退出
handlerThread.quit();
break;
default:
break;
}
return false;
}
}
HandlerThread的基本用法非常简单,只需几步操作即可在异步线程中执行耗时任务,从而避免主线程阻塞。需要注意的是,HandlerThread本身也是一个线程,要使其正常工作,必须调用start()函数。同时,Looper对象是在run()函数中创建的,只有当run()函数执行时,才会创建Looper对象并进行消息循环。
通过HandlerThread的getLooper()方法创建的Handler主要用于执行异步任务,因为它不在主线程中,无法进行UI更新。因此,它更适合用于处理一些耗时任务。
下面是关于HandlerThread的源码分析:
学会了使用HandlerThread,那么是否对其内部实现感兴趣呢?反正我是挺想了解的,不知道你是否也有同样的想法。如果你有兴趣,不妨和我一起探讨一下HandlerThread的内部原理吧。
```java
public class HandlerThread extends Thread { // 线程优先级
int mPriority; // 线程id
int mTid = -1; // 该线程所持有的Looper对象
Looper mLooper; // 线程内部的Handler,可以通过getThreadHandler()函数直接获取使用
private @Nullable Handler mHandler; // 拥有一个参数的构造函数
// 传入参数为线程名称,具有默认线程优先级
public HandlerThread(String name) { super(name); mPriority = Process.THREAD_PRIORITY_DEFAULT; }
// 拥有两个参数的构造函数
// 传入的参数name是线程名称,参数priority是线程优先级(线程优先级详见Process类)
public HandlerThread(String name, int priority) { super(name); mPriority = priority; }
protected void onLooperPrepared() { }
// 调用该线程的start()函数后,run()函数会被执行
// run()函数可以说是HandlerThread的核心,该函数内部会创建Looper进行消息循环
@Override
public void run() { // 获取该线程的id
mTid = Process.myTid(); // 为该线程创建Looper
Looper.prepare(); // 通过持有同步锁机制得到该线程的Looper对象
// 然后调用notifyAll()函数通知getLooper()函数Looper对象已经创建完成。
synchronized (this) { // 获取该线程的Looper对象
mLooper = Looper.myLooper(); // 唤醒在当前对象监视器上等待的所有线程
notifyAll();
} // 设置线程优先级
Process.setThreadPriority(mPriority); // 调用onLooperPrepared()函数,子类可以在消息循环之前做一些准备工作
onLooperPrepared(); // 开始消息循环
Looper.loop(); mTid = -1;
}
// 获取该线程的Looper对象
public Looper getLooper() { // 如果该线程不是isAlive,则直接返回null
if (!isAlive()) { return null; } // 通过持有同步锁机制判断当前是否创建了Looper对象
// 如果该线程没有start,即还没有创建Looper对象
// 则调用wait()函数,使当前对象上的线程进入等待,就是等待run()函数中的notifyAll()函数执行
synchronized (this) { while (isAlive() && mLooper == null) {
try { wait(); } catch (InterruptedException e) { }
} }
return mLooper; }
// 直接获取一个Handler对象
@NonNull
public Handler getThreadHandler() { if (mHandler == null) { // 创建Handler实例,参数为该线程的Looper
mHandler = new Handler(getLooper()); } return mHandler; }
// 退出消息循环,效率高,但不是线程安全的
public boolean quit() { Looper looper = getLooper(); if (looper != null) { //调用Looper对象的quit()函数退出消息循环,内部调用的是MessageQueue的quit(boolean safe)函数,传入参数为false looper.quit(); return true; } return false; }
//退出消息循环,效率低,但是线程安全的
public boolean quitSafely() { Looper looper = getLooper(); if (looper != null) { //调用Looper对象的quitSafely()函数退出消息循环 //内部调用的是MessageQueue的quit(boolean safe)函数,传入参数为true looper.quitSafely(); return true; } return false; }
//获取当前线程id
public int getThreadId() { return mTid; }
}
```
分析了HandlerThread的源码后,我们可以将其总结为以下几点:
1. HandlerThread有两个构造函数,分别是线程名称或线程名称和线程优先级。这使得用户可以根据需要创建具有特定名称和优先级的线程。
2. HandlerThread的本质就是一个线程,但不同于普通的线程,它的核心是run()函数。在run()函数内部,HandlerThread会创建一个Looper对象进行消息循环,使通过该Looper创建的Handler实例运行在该线程中。为了避免getLooper()获取到的Looper对象为空,采用了同步锁机制。具体来说,在getLooper()函数中,如果mLooper为null,则让当前对象上的线程进入等待,直到run()函数中创建好Looper对象后,唤醒在当前对象监视器上等待的所有线程,即getLooper()函数继续执行。
3. HandlerThread内部提供了一个Handler,其创建方式也是通过getLooper()函数获取该线程的Looper对象进行创建。不过该Handler只有在外部调用getThreadHandler()时才会进行创建。
4. HandlerThread有两种退出方式,一种不是线程安全的,但是效率高;一种是线程安全的,但是效率低。其最终调用的都是MessageQueue的quit(boolean safe)函数。
5. HandlerThread和Thread一样,都是调用start()函数开启线程。不过需要注意的是,HandlerThread的构造函数需要传入线程名称,而Thread的构造函数则不需要。
6. 继承自Thread的子类需要重写其run()函数,在其中执行任务;而HandlerThread的子类可以重写 onLooperPrepared()函数,在创建Looper对象之前做准备工作。