1.前言
这一篇是谈HandlerThread
这种异步任务的方式,HandlerThread
源码中会涉及一些关于Handler
和Looper
的内容。
2.HandlerThread使用
HandlerThread
的使用有以下几个步骤:
1. 创建HandlerThread对象
HandlerThread handlerThread = new HandlerThread("myHandlerThread");
2.开启HandlerThread
handlerThread.start();
3.创建HandlerThread的Handler,并复写其handleMessage方法
Handler handler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case type1: {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, Thread.currentThread().getName() + " type1 receive");
}
break;
case type2: {
for (percent = 0; percent <= 100; percent += 10) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, Thread.currentThread().getName() + " type2 " + percent + "% progress");
}
Log.d(TAG, Thread.currentThread().getName() + ":finish");
}
break;
}
}
};
4.通过Handler发送消息
Message message1 = Message.obtain();
message1.what = type1;
handler.sendMessage(message1);
Message message2 = Message.obtain();
message2.what = type2;
handler.sendMessage(message2);
5.使用完退出HandlerThread
handlerThread.quit();
运行结果日志:

3.HandlerThread运行原理
无论从HandlerThread
的名字还是它的使用方法来看HandlerThread
都是一个线程Thread
加上一个Handler
的组合,其实也确实如此。先从它的构造方法开始看。
/**
* 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.
*/
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
private @Nullable Handler mHandler;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
/**
* Constructs a HandlerThread.
* @param name
* @param priority The priority to run the thread at. The value supplied must be from
* {@link android.os.Process} and not from java.lang.Thread.
*/
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
......
}
首先看到它确实是继承了Thread
类,从HandlerThread
类上的注释可以看出HandlerThread
就是一个提供带有Looper
的线程便利类,可以通过这个Looper
创建Handler
。我们知道子线程中使用Handler
需要自己手动创建Lopper
,而使用HandlerThread
就不需要了,它里面已经帮我们创建好了Looper
。HandlerThread
它有两个重载的构造方法,构造方法中只做了两件事,就是这个线程设置名字和优先级。作为一个Thread
类想要使用它就必须调用start
方法开启线程,开启线程后就会执行它的run
方法,继续来看它的run
方法实现。
@Override
public void run() {
// 获取线程id
mTid = Process.myTid();
// 创建Looper
Looper.prepare();
synchronized (this) {
// 获取当前线程的Looper
mLooper = Looper.myLooper();
// 通知等待唤醒
notifyAll();
}
// 设置线程优先级
Process.setThreadPriority(mPriority);
// 开启Looper循环前的准备方法
onLooperPrepared();
// 开启轮询
Looper.loop();
// 将线程id修改为-1
mTid = -1;
}
run
方法中首先获取线程id
,然后就调用了Looper.prepare
方法创建一个Looper
,接着调用了Looper.myLooper
方法获取到了当前线程的Looper
。接着通过notifyAll
通知等带唤醒,这里的等待是在HandlerThread
的getLooper
方法里调用的wait
方法,getLooper
方法是为了获取该HandlerThread
中的Looper
。如果在没调用HandlerThread
的start
方法开启线程前就调用getLooper
方法就通过wait
方法暂时先进入等待,等到run
方法运行后再进行唤醒。唤醒之后run
方法中继续设置了构造函数中传入的优先级,接着调用了onLooperPrepared
方法,该方法是个空实现,该方法是为了在Looper
开启轮询之前如果要进行某些设置,可以复写该方法。最后调用Looper.loop
开启轮询。
public Looper getLooper() {
// 如果该线程isAlive为false直接返回null
if (!isAlive()) {
return null;
}
// 如果isAlive为true但mLooper为null,就进行等待直到mLooper被创建
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
onLooperPrepared
空实现。
protected void onLooperPrepared() {
}
HandlerThread
中还提供了两个退出的方法分别对应Looper
中的两个退出方法。
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
判断不为空后调用了Looper
中的对应的退出方法。使用完HandlerThread
后一定要记得调用退出方法停止Looper
,否则Looper
会一直轮询。
除了以上方法,HandlerThread
里还有一个getThreadHandler
获取持有当前线程Looper
的Hanlder
,不过这个方法是hide
隐藏的。
/**
* @return a shared {@link Handler} associated with this thread
* @hide
*/
@NonNull
public Handler getThreadHandler() {
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}

4.总结
以上就是HandlerThread
所有源码方法,可以发现HandlerThread
源码不多一共160多行,其实现的功能也不复杂,就是封装了一个带有Looper
的线程类,方便了我们做异步耗时任务和通信。不过使用还是有几个要注意的点:
- 因为
HandlerThread
只有一个线程,所以在连续不停的使用Handler
的发送消息时,任务只能一个一个进行。 - 为
HandlerThread
用完记得调用退出方法。 - 因为要使用
Handler
所以会容易发生内存泄漏。
网友评论