一、什么是HandlerThread及特点
HandlerThread本质就是一个Thread,在内部建立了消息队列机制(Looper、MessageQueue),具有消息循环的线程。
特点
- HandlerThread本质上一个线程类,它继承了Thread
- HandlerThread与自己的内部Looper对象,可以进行loop循环
- 通过获取looper对象传递给Handler对象,并可以在Handler的handleMessage方法中执行异步任务
- 与线程池重并发不同,handlerThread是一个串行队列,背后只有一个线程
- 优点就是不会堵塞,减少对信性能的消耗,缺点是不能同时进行多任务处理,需要等待处理,效率较低。
二、HandlerThread使用方法
1.创建HandlerThread线程,并在构造方法内传入线程名称,可以是任意字符串
HandlerThread mHandlerThread = new HandlerThread("Handler_Thread");
2.启动线程
mHandlerThread.start();
3.创建异步Handler对象,并构建Callback消息循环机制
mchildHandler = new Handler(mHandlerThread.getLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
//子线程内运行,此处不能更新UI
Log.d(TAG, "handleMessage: "+msg.what+" 线程名:"+Thread.currentThread().getName());
// mTvContent.setText(String.valueOf(msg.what));
//主线程Hanlder,发送消息通知更新UI
mUIHandler.sendMessage(msg);
return false;
}
});
以上步骤3构建一个完整异步Handler,将HandlerThread的Looper对接和Callback接口类作为参数传递到异步 mchildHandler,这样当前的mchildHandler对象就拥有HandlerThread的Looper对象,由于HandlerThread本身是异步线程,故Looper也绑定到异步线程中,所以handleMessage方法是处理异步耗时任务,当处理状态发生改变,需要UI线程的Handler发送消息通知更新UI。
mchildHandler = new Handler(mHandlerThread.getLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
//子线程内运行,此处不能更新UI
Log.d(TAG, "handleMessage: "+msg.what+" 线程名:"+Thread.currentThread().getName());
// mTvContent.setText(String.valueOf(msg.what));
//主线程Hanlder,通知主线程更新
Message message = Message.obtain();
message.what=msg.what;
mUIHandler.sendMessage(message);
return false;
}
});
模拟触发异步线程
for (int i = 0; i <= 10; i++) {
Message obtain = Message.obtain();
obtain.what = i;
mchildHandler.sendMessageAtTime(obtain,1000);
}
三、HandlerThread源码分析
首先我们从构造函数开始:
public class HandlerThread extends Thread {
//线程优先级
int mPriority;
int mTid = -1;
//当前线程所持有的Looper对象
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;
}
从源码可以看出HandlerThread是继承于Thread,本质上是一个线程,构造方法需要传入两个参数,一个是name指的是线程名称,一个是priority指的是线程的优先级,当创建了HandlerThread对象并开启线程start方法后,run方法就会马上执行,接下来看看run方法:
/**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
//在当前线程初始化一个Looper对象
Looper.prepare();
synchronized (this) {
//把初始化的Looper对象赋值给HandlerThread的内部mLooper
mLooper = Looper.myLooper();
//唤醒等待线程
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
从源码可以看出,当run方法被执行后,会调用Looper.prepare()创建一个Looper对象和MessageQueue对象并绑定了在当前线程(异步线程);这样才可以Looper对象以参数的形式传给Handler对象,从而确保Handler的hanldMessage方法是在子线程(异步线程)中执行的;接下来会把创建的Looper对象赋值给HandlerThread的内部变量mLooper,并通过notifyAll方法唤醒等待线程,最后调用loop方法,中间有个onLooperPrepared方法,可以重写此方法,也就是说在loop开启循环之前,可以做一些其他的操作。有个疑问就是在loop开启循环之前,为什么要唤醒等待线程呢,接下来看看getLooper方法:
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
* @return The looper .
返回此线程关联的Looper对象
*/
public Looper getLooper() {
//如果线程没有开启,则返回null
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
//如果线程被开启了,而且当前线程Looper为null时,此时会等待状态,直到mLooper被初始化并赋值后会被唤醒
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
当外部主线程调用getLooper方法,先会判断当前异步线程是否启动了,如果isAlive()为false ,说明线程未开启,会返回null,如果线程启动了,则会进入同步代码块内并判断mLooper是否为null,也就是内部变量mLooper是否被赋值,为null表示未赋值尚未初始化,进入等待状态,直到mLooper被赋值后,notifyAll方法会唤醒所以的等待线程 ,最后返回Looper对象。 之所以有个等待唤醒机制,主要getLooper方法是在主线程调用的,而mLooper是在子线程初始化赋值的,也就是我们无法保证不同线程访问同一个对象 是否被创建了,也就是说在调用getLooper方法来获取Looper对象时,无法确保mLooper已经被创建了 ,,故引入等待唤醒机制来确保线程创建开启后 并且Looper对象 也被创建后才可以成功返回mLooper
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;
}
线程的退出提供两个方法,一个quit,一个quitSafely,quitSafely相对更安全下。
quit:会清除MessageQueue中全部的消息,无论是延迟还是非延迟的消息都清除。
quitSafely:该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理完成后才停止Looper循环。