前言:我们知道在Android系统中,我们执行完耗时操作都要另外开启子线程来执行,执行完线程以后线程会自动销毁。想象一下如果我们在项目中经常要执行耗时操作,如果经常要开启线程,接着又销毁线程,这无疑是很消耗性能的?那有什么解决方法呢?
一般有两种:
- 使用线程池
- 使用HandlerThread
而HandlerThread是android系统帮我们封装的一个异步处理任务的Thread,并且内部封装了looper对象。
HandlerThread特点:
- HandlerThread本质上是一个线程类,它继承了Thread;
- HandlerThread有自己的内部Looper对象,可以进行looper循环;
- 通过获取HandlerThread的looper对象传递给Handler对象,可以在handleMessage方法中执行异步任务。
- 创建HandlerThread后必须先调用HandlerThread.start()方法,Thread会先调用run方法,创建Looper对象。
一、HandlerThread常规使用步骤
1.创建实例对象
HandlerThread handlerThread = new HandlerThread("downloadImage");
2.启动HandlerThread线程
handlerThread.start();
3.构建循环消息处理机制
/**
* 该callback运行于子线程
*/
class ChildCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message msg) {
//在子线程中进行相应的网络请求
//通知主线程去更新UI
mUIHandler.sendMessage(msg1);
return false;
}
}
4.构建异步handler
Handler childHandler = new Handler(handlerThread.getLooper(),new ChildCallback());
二、HandlerThread源码解析
先贴出完整的源码,HandlerThread的源码非常精简,只有不到100行:
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
/**
* 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.prepare();
//持有锁机制来获得当前线程的Looper对象
synchronized (this) {
mLooper = Looper.myLooper();
//发出通知,当前线程已经创建mLooper对象成功,这里主要是通知getLooper方法中的wait
notifyAll();
}
//设置线程的优先级别
Process.setThreadPriority(mPriority);
//这里默认是空方法的实现,我们可以重写这个方法来做一些线程开始之前的准备,方便扩展
onLooperPrepared();
Looper.loop();
mTid = -1;
}
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// 直到线程创建完Looper之后才能获得Looper对象,Looper未创建成功,阻塞
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return 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;
}
/**
* Returns the identifier of this thread. See Process.myTid().
*/
public int getThreadId() {
return mTid;
}
}
1.先分析构造方法
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
可以看出HandlerThread继续自Thread,有两个构造方法,构造方法的传递参数分别有一个和有两个,一个参数的构造方法的参数是name指的是线程的名称;两个参数的构造方法的参数一个是name指的是线程的名称,一个是priority指的是线程优先级;我们根据需要调用即可。
int mPriority;//线程优先级
int mTid = -1;
Looper mLooper;//当前线程持有的Looper对象
protected void onLooperPrepared() {
}
其中成员变量mLooper就是HandlerThread自己持有的Looper对象。onLooperPrepared()该方法是一个空实现,是留给我们必要时可以去重写的,但是注意重写时机是在Looper循环启动前。
2.再分析run方法
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll(); //唤醒等待线程
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
在创建HandlerThread对象后必须调用其start()方法才能进行其他操作,而调用start()方法后相当于启动了线程,也就是run方法将会被调用。因为HandlerThread是继承与Thread类的。
从代码我们可以看到Looper.prepare(),这里Looper对象被创建,这样我们再构建异步handler时才可以把Looper对象给Handler对象,进而确保Handler对象中的handleMessage方法是在异步线程执行的。
接着执行
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll(); //唤醒等待线程
}
这里唤醒了等待线程,原因就要看看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;
}
先用isAlive()判断线程是否启动,不然线程没启动就不存在Looper,自然返回空(null),如果判断true进行下个判断,Looper的创建是在子线程中执行的,而调用getLooper方法则是在主线程进行的,无法保证在调用getLooper方法时Looper已经被创建,所以要判断mLooper == null,是空的话就让getLooper方法先等待,让run方法创建完成之后notifyAll()来唤醒线程,这就是notifyAll()的原因了。
3.最后看看quit和quitSafely方法
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方法实际是调用Looper的quit方法,该方法主要是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送)还是非延迟消息。
quitSafely方法实际是调用Looper的quitSafely方法,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理完成后才停止Looper循环,quitSafely相比于quit方法安全的原因在于清空消息之前会派发所有的非延迟消息。
三、总结
HandlerThread对管理线程的开启和销毁做出了自己的封装,并且可以通过getLooper方法来传给Handler,可以在handleMessage方法中执行异步任务。避免了频繁开启多个线程和handler组合消耗大量性能的问题。