前言
在Android中,默认情况下,一个线程没有与它相关联的Looper。也就是说,默认情况下,我们无法创建与该线程相关联的Handler。HandlerThread是一个继承自Thread的线程类,它用于启动一个有Looper的线程。通过这个Looper,我们就可以创建与该线程相关联的Handler。通过Handler,我们就可以很方便地控制该线程执行我们的耗时任务。在Android系统中,很多地方都使用了HandlerThread。比如,IntentService、WifiScanningService、NetworkStatsService等等。
基础知识
HandlerThread的工作原理基于Android的消息机制。具体Android的消息机制的工作原理可以阅读 Android的消息机制源码分析 这篇文章。
基本用法
HandlerThread的用法非常简单。具体的示例代码如下所示:
HandlerThread thread = new HandlerThread("Thread name");
thread.start();
mLooper = thread.getLooper();
mHandler = new Handler(mLooper) {
@Override
public void handleMessage(Message msg) {
// execute task
}
};
首先,创建一个HandlerThread线程,并启动该线程。然后,获取该线程的Looper,并使用这个Looper来创建一个与该线程相关联的Handler。最后,通过这个Handler,我们就可以很方便地控制该线程执行我们的耗时任务。我们可以使用发送 Message 的方法,也可以使用投递 Runnable 的方法。
注意:
- 在获取HandlerThread线程的Looper之前一定要记得先启动线程。
- 在适当的时候要记得调用mLooper.quit()方法来退出Looper消息循环,结束HandlerThread线程。
- Looper从MessageQueue中循环地取出Message来处理,所以HandlerThread线程以队列的方式来执行多个任务。
源码分析
我们将按照HandlerThread基本用法的顺序来进行源码分析。
首先,我们要创建一个HandlerThread线程。所以,我们先来看HandlerThread的构造方法。
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
HandlerThread提供了两个构造方法。其中,name参数用来设置线程名,priority参数用来设置线程的优先级。第一个构造方法设置HandlerThread线程的优先级为线程的默认优先级。
然后,我们要启动该线程。所以,接下来我们来看HandlerThread的run()方法。
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
在run()方法中,首先调用了Looper.prepare()静态方法来创建一个与该线程相关联的Looper。Looper.prepare()静态方法下面的同步代码块用来同步该线程的Looper对象。接着设置了该线程的优先级。最后调用了Looper.loop()静态方法来启动该线程的消息循环。Looper.loop()静态方法是一个死循环,它循环地取出MessageQueue中的Message来处理。
接着,我们就可以获取该线程的Looper了。所以,接下来我们来看HandlerThread的getLooper()方法。
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
如果在启动线程之前调用了getLooper()方法,那么该方法将返回null。如果在启动线程之后调用了getLooper()方法,那么该方法将阻塞,直到Looper对象创建完毕。所以,我们要先启动HandlerThread线程,然后再通过getLooper()方法来获取与该线程相关联的Looper。获取到Looper之后,我们就可以创建与该线程相关联的Handler了。通过Handler,我们就可以很方便地控制该线程执行我们的耗时任务。
最后,在适当的时候我们要调用mLooper.quit()方法来退出Looper消息循环,结束HandlerThread线程。所以,最后我们来看HandlerThread的quit()方法。
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
quit()方法调用了Looper的quit()方法来退出Looper.loop()死循环,结束HandlerThread线程。
总结
HandlerThread的工作原理基于Android的消息机制。通过使用HandlerThread线程,我们可以很方便地控制该线程执行我们的耗时任务。
例子
这里举一个简单的例子来实践HandlerThread。项目源码地址:https://github.com/chongyucaiyan/HandlerDemo
我们创建了一个HandlerThread线程,并创建了一个与它相关联的Handler。通过这个Handler,我们发送Message来控制HandlerThread线程执行耗时任务。具体的代码和注释如下所示:
public class MainActivity extends AppCompatActivity {
private static final int DO_TASK1 = 1;
private static final int DO_TASK2 = 2;
private Looper mLooper;
private MyHandler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 创建HandlerThread线程
createHandlerThread();
}
private void createHandlerThread() {
// 创建HandlerThread线程,并启动该线程
HandlerThread thread = new HandlerThread("HandlerThread");
thread.start();
// 获取线程的Looper,创建与该线程相关联的Handler
mLooper = thread.getLooper();
mHandler = new MyHandler(mLooper);
// 发送任务消息
sendMessage();
}
private void sendMessage() {
Message message1 = mHandler.obtainMessage(DO_TASK1);
Message message2 = mHandler.obtainMessage(DO_TASK2);
mHandler.sendMessage(message1);
mHandler.sendMessage(message2);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 退出Looper消息循环
mLooper.quit();
}
private static class MyHandler extends Handler {
private static final String TAG = "MyHandler";
public MyHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case DO_TASK1:
// 执行任务1
doTask1();
break;
case DO_TASK2:
// 执行任务2
doTask2();
break;
default:
break;
}
}
private void doTask1() {
Log.i(TAG, "doTask1(), start");
Log.i(TAG, "doTask1(), thread name is " + Thread.currentThread().getName());
// 模拟耗时任务
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i(TAG, "doTask1(), end");
}
private void doTask2() {
Log.i(TAG, "doTask2(), start");
Log.i(TAG, "doTask2(), thread name is " + Thread.currentThread().getName());
// 模拟耗时任务
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i(TAG, "doTask2(), end");
}
}
}
运行应用,打印的信息如下图所示:
可以看到:
- 任务是在HandlerThread子线程中执行的。
- 任务2是在任务1执行完毕之后才开始执行的。这验证了HandlerThread线程是以队列的方式来执行多个任务的。
参考
- Android 7.1.1 (API level 25)
- https://developer.android.com/reference/android/os/HandlerThread.html