持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第12天,点击查看活动详情
HandlerThread是什么
首先,我们来看看,假如我们需要在handleMessage方法中,启用线程处理数据该怎么做:
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
new Thread(new Runnable() {
@Override
public void run() {
Log.i("处理数据的线程", Thread.currentThread().getName());
}
}).start();
}
};
Log.i("Handler所在的线程:", Thread.currentThread().getName());
handler.sendEmptyMessage(1);
结果输出:
I/Handler所在的线程:: main
I/处理数据的线程: Thread-17
但是,假如对于 Handler 机制比较了解的话,就会清楚,其实handleMessage方法的执行线程是Looper所在的线程,也就是说,在创建Handler的时候,假如传入其它线程的Looper,这时,handleMessage方法就在其它线程运行了。
class TestThread extends Thread {
private Looper looper;
@Override
public void run() {
Looper.prepare();
synchronized (this){
looper = Looper.myLooper();
notifyAll();
}
Looper.loop();
}
public Looper getLooper() {
if (looper == null) {
synchronized (this){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
return looper;
}
}
TestThread testThread = new TestThread();
testThread.start();
Handler handler = new Handler(testThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
Log.i("处理数据的线程", Thread.currentThread().getName());
}
};
Log.i("Handler所在的线程:", Thread.currentThread().getName());
handler.sendEmptyMessage(1);
结果输出:
I/Handler所在的线程:: main
I/处理数据的线程: Thread-15
TestThread分析
为什么TestThread写得这么复杂?
1、不能直接返回Looper.myLooper()
Looper.myLooper()返回的是当前线程中的Looper。若直接使用TestThread对象直接调用getLooper()返回Looper.myLooper(),那么返回的Looper为调用TestThread对象的线程的Looper,在本文例子中,则为返回主线程的Looper。
所以,需要在run()方法中初始化Looper,并将其值保存起来。
2、为什么要使用wait()和notifyAll()
因为Looper是在run()中进行初始化的,也就是说开启线程进行初始化,是异步的,在调用完TestThread的start()方法后,直接获取Looper有可能为null,所以需要使用
wait()和notifyAll()确保一定能够获取得到Looper对象。
嗯?是不是走远了?怎么这么久都还没讲到HandlerThread是什么。
其实,HandlerThread其实就是TestThread,只不过TestThread是简化版,但是核心逻辑基本一致。其继承于线程,并且对于新线程的Looper进行初始化并管理,所以,应该叫做LooperThread或许更加合适。
HandlerThread的简单使用
//初始化HandlerThread,传入的值为线程名字
HandlerThread handlerThread = new HandlerThread("ThreadName");
//记得一定要调用start(),其启动线程初始化Looper
handlerThread.start();
//初始化Handler并传入其它线程的Looper
Handler handler = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
Log.i("处理数据的线程", Thread.currentThread().getName());
}
};
Log.i("Handler所在的线程:", Thread.currentThread().getName());
//发送消息
handler.sendEmptyMessage(1);
//不使用后,要对于Looper进行退出操作
handlerThread.quit();
代码的注释已经写得够明白了,下面我们来看看输出结果:
I/Handler所在的线程:: main
I/处理数据的线程: ThreadName
HandlerThread源码分析(源码只保留关键部分,并非全部源码)
同样的,我们还是从简单使用的步骤,一步步分析:
HandlerThread初始化
HandlerThread handlerThread = new HandlerThread("ThreadName");
其源码:
public HandlerThread(String name) {
super(name);
···
}
super(name)是调用父类Thread的构造方法:
public Thread(String name) {
init(null, null, name, 0);
}
这个我们就不深究了,这属于Thread的方法,知道这是为了初始化Thread即可。
HandlerThread start() 的调用
handlerThread.start();
因为handlerThread本质也是一个Thread,所以,我们重点看看handlerThread对于run()的重写:
@Override
public void run() {
···
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
···
onLooperPrepared();
Looper.loop();
···
}
protected void onLooperPrepared() {
}
其实就是对于Looper进行初始化,并且把初始化后的Looper对象存储到mLooper中,并且调用onLooperPrepared()。
我们可以看到,onLooperPrepared()里面没有任何代码,用于用户重写该方法,执行些Looper初始化后的一些操作。
HandlerThread的Looper获取
handlerThread.getLooper()
public Looper getLooper() {
···
// 避免获取Looper的时候,Looper还未初始化(因为Looper是启动线程进行初始化)
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
HandlerThread的quit()
handlerThread.quit();
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
public void quit() {
//mQueue为MessageQueue
mQueue.quit(false);
}
结束消息队列,进而结束线程。
所以,使用完HandlerThread一定要记得调用quit(),否则线程将会一直存在!