[TOC]

概述

初次看到HandlerThread的名字,我们可能会联想到Handler和Thread这两个类,没错,它其实就是跟Handler和Thread有莫大的关系。HandlerThread继承自Thread,所以它本质上就是一个Thread,那既然是Thread,那么必然就应该是用来处理耗时任务,而且专门用来处理Handler中的耗时任务。

HandlerThread简介

看看官方对它的解释:

++Handy class for starting a new thread that has a looper.
The looper can then be used to create handler classes.
Note that start() must still be called.++

大致就是说HandlerThread可以创建一个带有looper的线程,looper对象可以用于创建Handler类来进行来进行调度,而且start()方法必须被调用。

在Android开发中,不熟悉多线程开发的人一想到要使用线程,可能就用

1
new Thread(){…}.start();

这样的方式。实质上在只有单个耗时任务时用这种方式是可以的,但若是有多个耗时任务要串行执行呢?那不得要多次创建多次销毁线程,这样导致的代价是很耗系统资源,容易存在性能问题。那么,怎么解决呢?

我们可以只创建一个工作线程,然后在里面循环处理耗时任务,创建过程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Handler mHandler;
private void createWorkerThread() {
new Thread() {
@Override
public void run() {
super.run();
Looper.prepare();
mHandler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(Message msg) {
......
}
};
Looper.loop();
}
}.start();
}

在该工作线程中,调用Looper.prepare()创建与当前线程绑定的Looper实例;

使用上面创建的Looper生成Handler实例;

调用Looper.loop()实现消息循环;

然后透过Looper的循环,在Handler的handlerMessage()中进行异步任务的循环处理。而这也正好是HandlerThread的实现。

关于源码,我们后面再进行解析。

我们先来看一下HandlerThread的使用

HandlerThread使用步骤

创建HandlerThread实例对象

1
2
3
4
5
6
7
/**
*HandlerThread共有两个构造函数,一个可以设置优先级
*/
HandlerThread handlerThread = new HandlerThread("Handler Thread");
// 线程名参数、线程的优先级
HandlerThread handlerThread = new HandlerThread("Handler Thread",Process.THREAD_PRIORITY_DEFAULT);

HandlerThread默认有两个构造函数,提供了线程名参数和线程优先级参数的设置。

启动HandlerThread线程

1
handlerThread.start();

通过start()方法就可以启动一个HandlerThread了,该线程会不断地循环运行。

通过Handler构建循环消息处理机制。

1
2
3
4
5
6
7
Handler workderHandler = new Handler(handlerThread.getLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
//运行在工作线程(子线程)中,用于实现自己的消息处理
return true;
}
});

通过将HandlerThread绑定的Looper对象传递给Handler作为参数,构建一个异步的Handler对象,为了能实现耗时任务的异步执行,我们重写了Handler的Callback接口的handleMessage()方法,当然也可以不重写该方法,而通过post()方法进行耗时任务操作。

1
2
3
4
5
6
7
Handler workderHandler = new Handler(handlerThread.getLooper());
workderHandler.post(new Runnable() {
@Override
public void run() {
//运行在工作线程(子线程)中,用于实现自己的消息处理
}
});

最后,我们就可以通过调用workerHandler以发送消息的形式发送耗时任务到工作线程HandlerThread中去执行,实际上就是在Handler.Callback里的handleMessage()中执行。

这里要注意,在创建Handler作为HandlerThread线程消息执行者的时候必须先调用start()方法,因为创建Handler所需要的Looper参数是从HandlerThread中获得的,而Looper对象的赋值又是在HandlerThread的run()方法中创建。

HandlerThread源码解析

我们来看下它的构造函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* Handy class for starting a new thread that has a looper.
* The looper can then be used to create handler classes.
* Note that start() must still be called.
*/
public class HandlerThread extends Thread {
int mPriority; //线程优先级
int mTid = -1; //当前线程id
//当前线程持有的Looper对象
Looper mLooper;
// 只需要传入线程的名称
public HandlerThread(String name) {
//调用父类默认的方法创建线程
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
//带优先级参数的构造方法
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
...............
}

从代码中得知,HandlerThread带有两个构造函数,可传递两个参数,一个参数是name,指的是线程的名称,
另一个参数是priority,指的是线程优先级。
线程的优先级的取值范围为-20到19。优先级高的获得的CPU资源更多,反之则越少。-20代表优先级最高,19最低。

我们可以根据自己的需要去设置线程的优先级,也可以采用默认的优先级,HandlerThread的默认优先级是Process.THREAD_PRIORITY_DEFAULT,具体值为0。该优先级是再run()方法中设置的,我们看它的run()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class HandlerThread extends Thread {
/**
* 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(); //获得当前线程的id
Looper.prepare(); //准备循环条件
//通过锁机制来获得当前线程的Looper对象
synchronized (this) {
mLooper = Looper.myLooper();
//唤醒等待线程
notifyAll();
}
//设置当前线程的优先级
Process.setThreadPriority(mPriority);
//在线程循环之前做一些准备工作(子类可实现也可不实现)
// 我们一般可以在这个实现里面进行初始化HandlerThread绑定的Handler
onLooperPrepared();
//启动loop
Looper.loop();
mTid = -1;
}
}

run()方法主要是通过Looper.prepare()和Looper.loop()构造了一个循环线程。这里要注意,在创建HandlerThread对象后必须调用其start()方法才能进行run()方法体的执行。

在Looper.prepare()执行后,Looper对象会被创建,然后通过同步锁机制,将Looper对象赋值给HandlerThread的内部变量mLooper,并通过notifyAll()方法去唤醒等待线程。接着为线程赋予优先级,然后执行onLooperPrepared()方法,该方法是一个空实现,留给我们必要的时候去重写的,主要用来做一些初始化工作。最后通过执行Looper.loop()在线程中启动消息队列。

我们看到在run()方法中进行了唤醒等待线程,为什么要这么做呢?答案就在getLooper()方法中:

HandlerThread的使用场景

HandlerThread是自带消息循环的线程,比较适合那种长时间在后台执行的消息任务队列。比如,我们在进行跨进程通信的时候维持消息传输的生产者、消费者队列。因为长时间在后台运行,他才能不断地从消息队列中取出消息来进行消息的分发,耗时处理。

HandlerThread比较适用于单线程+异步队列的场景,比如IO读写操作数据库、文件等,耗时不多而且也不会产生较大的阻塞。

对于网络IO操作,HandlerThread并不适合,因为它只有一个线程,还得排队一个一个等着。

这里单线程+异步队列模型的使用场景还举几个例子:

  • 1.应用刚启动时的数据初始化操作,如果开启多个线程同时执行,有可能争夺UI线程的CPU执行时间,造成卡顿,而使用该模型,通过设置优先级就可以将同步工作顺序的执行,而又不影响UI的初始化;
  • 2.从数据库中读取数据展现在ListView中,通过Handler的postAtFrontOfQueue方法,快速将读取操作加入队列前端执行,必要时返回给主线程更新UI;
  • 3.HandlerThread应用在IntentService中,IntentService是一个需要长时间执行任务Service,优先级要比线程高。
  • 4.我们在进行跨进程通信的时候维持消息传输的生产者、消费者队列。因为长时间在后台运行,他才能不断地从消息队列中取出消息来进行消息的分发,耗时处理。