不达成功誓不休 — 程端礼

写在前面

“送走四月,喜迎五月”,今年的五一小长假简直舒服到了极致,牺牲两个周六换来的四天假期。是时候出去浪了,打开微博发现不妙,吓得我赶紧躺床上又睡了一觉...

入职后经常加班,仅有的私人时间也没有心思学习。不过我还是告诉自己在忙也不能忘了充实自己。趁着今天有兴致,阅读了一下HandlerThread的源码。

用法

首先,我们来了解一下HandlerThread。

Q:什么是HandlerThread?

A:HandlerThread继承自Thread,可以说其是一个线程。并且有一个Looper对象进行消息循环。

接下来,我们看一下HandlerThread是如何执行异步任务的。

// 创建HandlerThread对象,参数为该线程的Name

HandlerThread handlerThread = new HandlerThread("work");

// 调用HandlerThread的start()函数开启线程

handlerThread.start();

// 通过调用HandlerThread的getLooper()函数得到Looper对象,并创建Handler用于执行异步任务

Handler workHandler = new Handler(handlerThread.getLooper(), new WorkHandlerCallback());

// 在主线程中创建Handler,用于更新UI(主线程中有一个Looper)

final Handler uiHandler = new Handler(new UIHandlerCallback());

// 发送一个异步任务的消息

Message msg = workHandler.obtainMessage();

msg.what = 0x01;

workHandler.sendMessage(msg);

private class WorkHandlerCallback implements Handler.Callback {

@Override

public boolean handleMessage(Message msg) {

switch (msg.what) {

case 0x01:

// 开始执行异步任务

int sum = 0;

for (int i = 0; i < 100; i++) {

sum += i;

}

// 异步任务执行成功后,告知UI线程更新

Message message = uiHandler.obtainMessage();

message.what = 0x02;

message.obj = sum + "";

uiHandler.sendMessage(message);

break;

default:

break;

}

return true;

}

}

private class UIHandlerCallback implements Handler.Callback {

@Override

public boolean handleMessage(Message msg) {

switch (msg.what) {

case 0x02:

// 更新UI

textView.setText((String) msg.obj);

// 异步任务做完以后,不再需要HandlerThread,就调用quit()函数退出

handlerThread.quit();

break;

default:

break;

}

return false;

}

}

HandlerThread的基本用法非常简单,只需几步操作即可在异步线程中执行耗时任务,从而避免主线程阻塞。需要注意的是,HandlerThread本身也是一个线程,要使其正常工作,必须调用start()函数。同时,Looper对象是在run()函数中创建的,只有当run()函数执行时,才会创建Looper对象并进行消息循环。

通过HandlerThread的getLooper()方法创建的Handler主要用于执行异步任务,因为它不在主线程中,无法进行UI更新。因此,它更适合用于处理一些耗时任务。

下面是关于HandlerThread的源码分析:

学会了使用HandlerThread,那么是否对其内部实现感兴趣呢?反正我是挺想了解的,不知道你是否也有同样的想法。如果你有兴趣,不妨和我一起探讨一下HandlerThread的内部原理吧。

```java

public class HandlerThread extends Thread { // 线程优先级

int mPriority; // 线程id

int mTid = -1; // 该线程所持有的Looper对象

Looper mLooper; // 线程内部的Handler,可以通过getThreadHandler()函数直接获取使用

private @Nullable Handler mHandler; // 拥有一个参数的构造函数

// 传入参数为线程名称,具有默认线程优先级

public HandlerThread(String name) { super(name); mPriority = Process.THREAD_PRIORITY_DEFAULT; }

// 拥有两个参数的构造函数

// 传入的参数name是线程名称,参数priority是线程优先级(线程优先级详见Process类)

public HandlerThread(String name, int priority) { super(name); mPriority = priority; }

protected void onLooperPrepared() { }

// 调用该线程的start()函数后,run()函数会被执行

// run()函数可以说是HandlerThread的核心,该函数内部会创建Looper进行消息循环

@Override

public void run() { // 获取该线程的id

mTid = Process.myTid(); // 为该线程创建Looper

Looper.prepare(); // 通过持有同步锁机制得到该线程的Looper对象

// 然后调用notifyAll()函数通知getLooper()函数Looper对象已经创建完成。

synchronized (this) { // 获取该线程的Looper对象

mLooper = Looper.myLooper(); // 唤醒在当前对象监视器上等待的所有线程

notifyAll();

} // 设置线程优先级

Process.setThreadPriority(mPriority); // 调用onLooperPrepared()函数,子类可以在消息循环之前做一些准备工作

onLooperPrepared(); // 开始消息循环

Looper.loop(); mTid = -1;

}

// 获取该线程的Looper对象

public Looper getLooper() { // 如果该线程不是isAlive,则直接返回null

if (!isAlive()) { return null; } // 通过持有同步锁机制判断当前是否创建了Looper对象

// 如果该线程没有start,即还没有创建Looper对象

// 则调用wait()函数,使当前对象上的线程进入等待,就是等待run()函数中的notifyAll()函数执行

synchronized (this) { while (isAlive() && mLooper == null) {

try { wait(); } catch (InterruptedException e) { }

} }

return mLooper; }

// 直接获取一个Handler对象

@NonNull

public Handler getThreadHandler() { if (mHandler == null) { // 创建Handler实例,参数为该线程的Looper

mHandler = new Handler(getLooper()); } return mHandler; }

// 退出消息循环,效率高,但不是线程安全的

public boolean quit() { Looper looper = getLooper(); if (looper != null) { //调用Looper对象的quit()函数退出消息循环,内部调用的是MessageQueue的quit(boolean safe)函数,传入参数为false looper.quit(); return true; } return false; }

//退出消息循环,效率低,但是线程安全的

public boolean quitSafely() { Looper looper = getLooper(); if (looper != null) { //调用Looper对象的quitSafely()函数退出消息循环 //内部调用的是MessageQueue的quit(boolean safe)函数,传入参数为true looper.quitSafely(); return true; } return false; }

//获取当前线程id

public int getThreadId() { return mTid; }

}

```

分析了HandlerThread的源码后,我们可以将其总结为以下几点:

1. HandlerThread有两个构造函数,分别是线程名称或线程名称和线程优先级。这使得用户可以根据需要创建具有特定名称和优先级的线程。

2. HandlerThread的本质就是一个线程,但不同于普通的线程,它的核心是run()函数。在run()函数内部,HandlerThread会创建一个Looper对象进行消息循环,使通过该Looper创建的Handler实例运行在该线程中。为了避免getLooper()获取到的Looper对象为空,采用了同步锁机制。具体来说,在getLooper()函数中,如果mLooper为null,则让当前对象上的线程进入等待,直到run()函数中创建好Looper对象后,唤醒在当前对象监视器上等待的所有线程,即getLooper()函数继续执行。

3. HandlerThread内部提供了一个Handler,其创建方式也是通过getLooper()函数获取该线程的Looper对象进行创建。不过该Handler只有在外部调用getThreadHandler()时才会进行创建。

4. HandlerThread有两种退出方式,一种不是线程安全的,但是效率高;一种是线程安全的,但是效率低。其最终调用的都是MessageQueue的quit(boolean safe)函数。

5. HandlerThread和Thread一样,都是调用start()函数开启线程。不过需要注意的是,HandlerThread的构造函数需要传入线程名称,而Thread的构造函数则不需要。

6. 继承自Thread的子类需要重写其run()函数,在其中执行任务;而HandlerThread的子类可以重写 onLooperPrepared()函数,在创建Looper对象之前做准备工作。