HandlerThread源码分析
概述
在之前的文章中介绍过Android应用层的消息机制【点这里 】,也介绍了利用Handler机制实现多线程调度的AsyncTask,接下来介绍一个新成员HandlerThread。从字面上可以看出,HandlerThread可能是与Thread与Handler应用相关的,HandlerThread也确实是封装了Handler功能的Thread,接下来我们从其基本使用,主要用途,源码等方面来介绍HandlerThread。
基本使用
一般使用HandlerThread步骤如下:
@Override
public void onCreate() {
// 1.创建线程
HandlerThread thread = new HandlerThread("myHandlerThread");
// 2.启动线程
thread.start();
}
// 3.创建与当前线程关联的handler,这里使用了HandlerThread创建的looper
Handler handler = new Handler(thread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 消息处理,非主线程消息
}
});
@Override
public void onStart(@Nullable Intent obj, int arg) {
// 4.利用Handler向绑定的线程发送消息
Message msg = handler.obtainMessage();
msg.arg1 = arg;
msg.obj = obj;
handler.sendMessage(msg);
}
@Override
public void onDestroy() {
// 5.调用生命周期结束,停止消息队列循环
thread.quitSafely();
}
对HandlerThread使用步骤大概如上述5点,使用比较简单,后续的源码分析时,会简单介绍为什么是这些步骤。
主要用途
HandlerThread的主要用途与注意点如下:
- HandlerThread将loop转到子线程中处理,主要目的是分担MainLooper的工作量,降低了主线程的压力,使主界面更流畅。
- 一个线程起到多个线程工作的特点,处理任务是串行执行,按消息发送顺序进行处理。HandlerThread本质是一个线程,在线程内部,代码是串行处理的。
- HandlerThread不适合处理类似网络I/O操作这样的并发任务,而且效率可能比较低,适合轻量级任务使用。
HandlerThread最典型的应用就是IntentService,我们都知道IntentService是特殊的Service,其内部就是使用HandlerThread来实现子线程处理任务的。
源码分析
为了更好的理解HandlerThread的使用,下面我们来了解下源码。
在文章开头我们介绍了HandlerThread的基本使用步骤,我们就按步骤的实现一一来看下源码,第一步我们创建了对象,所以先看下构造函数。
public class HandlerThread extends Thread {
Looper mLooper;
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,因此它具备线程的基本使用功能,其构造方法有两个,一个是单参数方法可设置线程名,一个是双参数方法可设置线程名与线程优先级,构造方法其实没做什么工作。接下来看第二步,调用了start()方法启动了线程,所以我们看看run()方法里做了什么。
@Override
public void run() {
// 线程标识
mTid = Process.myTid();
// 准备looper
Looper.prepare();
// 获取looper对象,注意这里的同步块
synchronized (this) {
// 获取looper
mLooper = Looper.myLooper();
notifyAll();
}
// 设置线程优先级
Process.setThreadPriority(mPriority);
// 钩子函数
onLooperPrepared();
// 消息队列启动循环
Looper.loop();
// 为了更好的回收loop,标识重置
mTid = -1;
}
我们都知道,要在子线程中使用Looper必须做上述的调度,HandlerThread帮我们已经封装好了。值得注意的是代码中的同步块,它的作用其实是为了后面获取使用Looper准备的,所以我们继续看第三步Handler创建时getLooper()方法。
public Looper getLooper() {
// 1.如果线程没start则直接返回空
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
// 2.如果线程start了则等待looper创建好
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
结合第二步骤源码中的同步块,就可以看到,当尝试使用HandlerThread中的Looper创建Handler时,HandlerThread内部会利用信号量与同步机制来保证消息队列Looper的创建与运行,只有线程正常运行了,才能正常获取到Looper对象。
第四步则是利用消息机制实现任务调度,这里不再赘述。
第五步主要目的是退出当前线程的Looper,因为没有用的情况下,开着Looper会消耗资源。查看HandlerThread源码,其实提供了两个停止Looper的方法,如下:
/**
* Quits the handler thread's looper.
* <p>
* Causes the handler thread's looper to terminate without processing any
* more messages in the message queue.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p class="note">
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*
* @see #quitSafely
*/
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
/**
* Quits the handler thread's looper safely.
* <p>
* Causes the handler thread's looper to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* Pending delayed messages with due times in the future will not be delivered.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p>
* If the thread has not been started or has finished (that is if
* {@link #getLooper} returns null), then false is returned.
* Otherwise the looper is asked to quit and true is returned.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*/
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
这两个方法的注释都有点长,大概意思是表达quit()方法调度是不安全的,他不会处理消息队列中剩余的消息就停止了队列循环;quitSafely()方法则是会处理调停止前还在队列中的消息,然后停止循环。具体调哪个方法,就要跟进使用场景来看了,读者可以自己权衡。
就此对于HandlerThread源码分析就此结束,由于笔者技术有限,如果分析过程中有出现错误,欢迎指出,谢谢。