简介
HandlerThread
继承自
Thread
,内部封装了
Looper
。调用
handlerThread.start()
方法之后,内部就会创建一个
Looper
和
MessageQueue
,我们通过这个
Looper
对象来实例化一个
Handler
,然后我们就可以在其他线程中使用这个
Handler
了。
1.使用传统的 Thread + Handler 的方式实现线程间通信
我们需要自己去实现Looper对象,然后在Looper循环中创建Handler对象,从而接收Handler消息执行相应任务。
public Handler threadHandler;
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
threadHandler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 处理消息,因为这个方法是在子线程调用,所以要在这执行耗时任务
}
};
Looper.loop();
}
}).start();
2.使用HandlerThread的方式实现线程间通信
只需先创建一个
HandlerThread
实例,然后调用
handlerThread.start()
创建
Looper
对象,再通过
handlerThread.getLooper()
获取
HandlerThread
中的
Looper
对象,根据这个
Looper
对象,来创建我们的
Handler
,来处理子线程中的业务逻辑。
HandlerThread handlerThread = new HandlerThread("handler-thread");
handlerThread.start(); // 必须在Handler创建前调用,因为线程start后才会创建Looper
Handler threadHandler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 处理消息,在子线程调用,所以要在这执行耗时任务
}
};
实现原理
public class HandlerThread extends Thread {
int mPriority; // 线程优先级
int mTid = -1; // 线程Id
Looper mLooper; // Looper循环器对象
private @Nullable Handler mHandler; // Handler 对象
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
/**
* 开启Looper循环之前的准备工作
* 复写之后,添加自己的逻辑
*/
protected void onLooperPrepared() { }
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
/**
* 此方法返回与此线程关联的循环器。如果这个线程没有启动或者
* 因为任何原因isAlive()返回false,这个方法将返回null。
* 如果这个线程已经启动,这个方法将阻塞,直到循环器初始化为止。
*/
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// 如果线程已经启动,则会等待,直到创建了循环器。
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
/**
* 返回一个已经与此线程进行关联的 Handler对象。
*/
@NonNull
public Handler getThreadHandler() {
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}
/**
* 这是退出循环器Looper的方法,一旦调用了quit()方法,消息队列中的消息将会立即停止处理,
* 并且之后发来的所有消息都会失败。比如sendMessage()方法会返回false。
* 这是不安全的一种退出方式。即不管是否正在处理消息,直接移除所有回调。
*/
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
/**
* 安全的退出Looper循环,
* 会等待正在处理的消息处理完后再退出。
*/
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
/**
* 获取线程Id
*/
public int getThreadId() {
return mTid;
}
}
HandlerThread中的优先级Priority
合理的分配
HandlerThread
线程的优先级,一定程度的优化线程。
它默认的优先级是:
process.THREAD_PRIORITY_DEFAULT
,其值为
“0”
。
priority 的取值范围是 “-20 ~ 19”;数值越小,优先级越高。优先级越高的线程,获的的CPU资源越多,反之越少。但是作为工作线程的
HandlerThread
是没有必要设置这么高的优先级的,因而需要我们根据需求,来设置合理的优先级别。
HandlerThread的使用场景
由于它里面封装了
Looper
,所以属于
Thread + Looper
的场景。子线程中执行耗时任务,多任务的操作。
适用于本地IO操作:
可以使用HandlerThread来处理本地的IO读写的工作,因为本地IO操作大多数耗时都是毫秒级的,所以对于HandlerThread这种单线程 + 队列的形式,不会产生阻塞。我们也可以使用postAtFrontQueue() 方法来对本地IO读取操作,这样可以快速将读取操作加入到队列前面执行。比如:从数据库中读取数据显示到ListView列表中。
不适用于网络IO操作:
由于HandlerThread是单线程同步队列的,所以不适合处理网络IO的操作。
HandlerThread优缺点
优点:只需开启一个线程,就可以执行多个任务。
缺点:虽然支持多任务,但是HandlerThread是同步的,不能实现并发。
所以如果其中某一个线程 任务执行时间过长,就会导致后续的任务都会被延迟处理。
注意事项
当
HandlerThread
使用完成之后,需要调用其
quitSafe()
或者
quit()
方法来结束线程。
quitSafe() 会等待正在处理的消息处理完后再退出;
quit() 不管是否正在处理消息,都会直接移除所有回调。