不达成功誓不休 — 程端礼
写在前面
“送走四月,喜迎五月”,今年的五一小长假简直舒服到了极致,牺牲两个周六换来的四天假期。是时候出去浪了, 打开微博发现不妙,吓得我赶紧躺床上又睡了一觉...
入职后经常加班,仅有的私人时间也没有心思学习。不过我还是告诉自己在忙也不能忘了充实自己。趁着今天有兴致,阅读了一下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)
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 = mUiHandler.obtainMessage();
message.what = 0x02;
message.obj = sum + "";
mUiHandler.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,那么就不想知道它的内部是如何实现的吗?反正我是挺想的,也不知道你想不想,咱也不知道,咱也不敢问,有兴趣的童鞋就和我一起探其究竟吧。
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;
}
// 该函数为空实现,子类可以重写该函数,在Looper进行消息循环之前调用
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的源码后,总结为以下几点:
- HandlerThread有两个构造函数,分别是线程名称或线程名称和线程优先级。
- HandlerThread的本质就是一个线程,但不同于线程,它的核心就是run()函数,它在run()函数内部创建一个Looper对象进行消息循环,使通过该Looper创建的Handler实例运行在该线程中。为了避免getLooper()获取到的Looper对象为空,采用了同步锁机制,即在getLooper()函数中若mLooper为null,则让当前对象上的线程进入等待,直到run()函数中创建好Looper对象后,唤醒在当前对象监视器上等待的所有线程,即getLooper()函数继续执行。
- HandlerThread内部提供了一个Handler,其创建方式也是通过getLooper()函数获取该线程的Looper对象进行创建,不过该Handler只有在外部调用getThreadHandler()时才会进行创建。
- HandlerThread有两种退出方式,一种不是线程安全的,但是效率高;一种是线程安全的,但是效率低;其最终调用的都是MessageQueue的quit(boolean safe)函数。
- HandlerThread和Thread一样,都是调用start()函数开启线程。
- HandlerThread的构造函数需要传入线程名称,而Thread的构造函数则不需要。
- 继承自Thread的子类需要重写其run()函数,在其中执行任务;而HandlerThread的子类可以重写 onLooperPrepared()函数,在创建Looper对象之前做准备工作。
网友评论