HandlerThread,顾名思义,是将Thread与Handler结合的一种新型线程类。它本质上仍然是一个线程,但在内部嵌套了一个与之相关的Handler,从而实现了轻量级的异步处理。相较于普通线程,HandlerThread具有以下特点:
1. 普通线程类:HandlerThread的使用方法与普通线程类似。用户需要创建一个新的HandlerThread对象,并通过start()方法来启动该线程。
2. 内部嵌套了Handler:HandlerThread包含一个与其关联的Handler对象,这使得线程之间的数据交互变得更加简便。
3. 串行运行:为了实现任务的串行阻塞队列,HandlerThread内部采用了Looper机制。
下面我们来看一个简单的使用示例:
```java
// 2.1 作为线程使用
// 与普通线程相比,HandlerThread的优势在于可以在其中创建工作Handler对象。
// 在异步任务结束后,可以将消息发送到工作区的Handler的handleMessage方法中。
// 而普通的线程则需要手动维持Looper的prepare()和loop()方法。
public class MainActivity extends AppCompatActivity {
private static final int MY_HANDLER_ID = 1;
private Handler mMyHandler;
private Runnable mMyRunnable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMyHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MY_HANDLER_ID:
// 处理消息
break;
}
return true;
}
});
mMyRunnable = new Runnable() {
@Override
public void run() {
// 执行异步任务
Message message = Message.obtain();
message.what = MY_HANDLER_ID;
mMyHandler.sendMessage(message);
}
};
HandlerThread myThread = new HandlerThread("MyThread");
myThread.start(); // 开启线程并关联工作Handler
mMyRunnable.run(); // 执行异步任务
}
}
```
```java
// 创建和执行MYHandlerThread类,添加线程名称
MYHandlerThread myHandlerThread = new MYHandlerThread("xiaohan");
myHandlerThread.start();
class MYHandlerThread extends HandlerThread {
public MYHandlerThread(String name) {
super(name);
}
// 重写onLooperPrepared()方法
@Override
protected void onLooperPrepared() {
super.onLooperPrepared();
// 执行耗时任务
SystemClock.sleep(3 * 1000);
}
// 任务结束,将结果发送至工作区的Handler中
private Handler subHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
String result = (String) msg.obj;
Log.i(TAG, "handleMessage: 子线程中的Handler " + result);
}
};
@Override
public void run() {
// 在run方法中执行耗时任务,并将结果发送至subHandler中进行处理
onLooperPrepared();
}
}
// 结束线程
mHandlerThread.quit();
```
以下是重构后的代码:
```java
// 1. 创建HandlerThread对象,填写线程名称,通过start()启动该线程
mHandlerThread = new HandlerThread("xiaohan");
mHandlerThread.start();
// 2. 创建WorkHandle工作区,处理不同的异步任务
mWorkHandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_THREAD1:
Log.i(TAG, "handleMessage: 子线程处理任务1");
break;
case UPDATE_THREAD2:
Log.i(TAG, "handleMessage: 子线程处理任务2");
break;
}
}
};
// 3. 发送消息到工作区处理
Message message = mWorkHandler.obtainMessage();
message.what = UPDATE_THREAD1;
message.obj = num + "";
mWorkHandler.sendMessage(message);
// 4. 结束线程
mHandlerThread.quit();
```
. 创建Handler并绑定Looper
要创建一个Handler并将其与当前线程的Looper对象绑定,可以使用getLooper()方法获取当前线程的Looper对象,然后在创建Handler时与其绑定。这样,该Handler将持有Looper以及MessageQueue,使其能够正常工作。
3.2 开启HandlerThread线程
要启动HandlerThread线程,可以调用其start()方法。这将开始执行线程的run()方法,如下所示:
```java
// 创建HandlerThread,添加线程名称
HandlerThread handlerThread = new HandlerThread("MyThread");
handlerThread.start(); // 开启线程
// 通过getLooper()获取当前线程的Looper对象,在创建Handler时与其绑定
Looper looper = handlerThread.getLooper();
Handler handler = new Handler(looper);
```
3. 发送异步任务
根据需要,可以将异步的Message信息发送到工作区的handleMessage方法中,以便在该方法中进行耗时操作。
3.1 源码分析
对源码的分析,参考第二部分中使用的步骤,可以看出以上两种方式都大致分为5步。下面参考2.2中的执行流程分析如下:
3.1 创建HandlerThread,添加线程名称
HandlerThread的构造方法如下:
```java
//线程优先级 int mPriority; //线程号 int mTid = -1; //Looper对象 Looper mLooper; //Handler对象 private @Nullable Handler mHandler; //只包含线程名称的构造,其默认线程优先级为默认的:0 public HandlerThread(String name) { super(name); mPriority = Process.THREAD_PRIORITY_DEFAULT; } //包含线程名称和优先级的构造 public HandlerThread(String name, int priority) { super(name); mPriority = priority; }
```
本部分主要是新建HandlerThread类,设置进程名称和优先级。
3.2 开启HandlerThread线程
调用start()后开始执行线程的run()方法,如下所示:
```java
handlerThread.start(); // 开启线程
```
重构后的内容如下:
```java
@Override
public void run() {
// 获取线程号
mTid = Process.myTid();
// 调用prepare()方法,准备MessageQueue
prepare();
// 阻塞等待,查看Looper是否准备完成,其notifyAll()主要针对下面的getLooper()中的wait()方法
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
// 设置线程优先级
Process.setThreadPriority(mPriority);
// 如果需要自定义处理,可以重写onLooperPrepared()方法
onLooperPrepared();
// 开启loop循环,执行消息的存储和派发
startLooper();
mTid = -1;
}
private void prepare() {
// 实现准备MessageQueue的逻辑
private void onLooperPrepared() {
// 实现自定义处理的逻辑,如异步操作等
}
private void startLooper() {
Looper.loop();
}
```
以下是重构后的内容:
```java
mWorkHandler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_THREAD1:
Log.i(TAG, "handleMessage: 子线程处理1");
break;
case UPDATE_THREAD2:
Log.i(TAG, "handleMessage: 子线程处理2");
break;
}
}
};
```
主要流程如下:
1. 准备Looper对象:通过`Looper.prepare()`方法开始准备Looper对象。
2. 阻塞等待:使用`synchronized`关键字对代码块进行加锁,以便在准备Looper对象时不会被其他线程干扰。当Looper对象准备完成后,调用`notifyAll()`方法通知所有等待的线程,并释放锁。
3. 获取Looper对象:通过`getLooper()`方法获取已准备好的Looper对象。在该方法中,同样使用`synchronized`关键字对代码块进行加锁,然后使用`while`循环不断检查是否已经完成了Looper的准备。如果Looper尚未准备好,就调用`wait()`方法让当前线程等待;如果Looper已经准备好,就调用`notifyAll()`方法通知所有等待的线程并返回Looper对象。
4. 发送消息:在获取到已准备好的Looper对象后,可以将其用于创建Handler对象,然后通过Handler对象的`sendMessage()`方法发送消息,执行异步的任务处理。
5. 结束线程:调用`HandlerThread`的`quit()`方法或`quitSafely()`方法来结束线程。其中,`quit()`方法是非线程安全的,可能会导致执行效率降低;而`quitSafely()`方法是线程安全的,但可能引起一定的性能损失。
//HandlerThread 重构后的方法:
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;
}
//Looper 重构后的方法:
public void quit(boolean value) {
mQueue.quit(value);
}
MessageQueue的quit方法用于退出消息队列,根据参数safe来判断是否线程安全。如果safe为true,则在移除所有消息之前先移除所有未来的消息;如果safe为false,则直接移除所有消息。
在移除所有消息的过程中,分为线程不安全和线程安全两种方式。线程不安全的方式是遍历所有消息,并置空;线程安全的方式是在回收消息时,判断消息的时间,如果该消息未处理完成则等待处理,否则直接处理(与线程不安全类似)。
以下是重构后的代码:
```java
// MessageQueue
void quit(boolean safe) {
// 判断是否为线程安全
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
}
// 移除所有消息(线程不安全)
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
// 移除所有未来消息(线程安全)
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
if (p.when > now) {
removeAllMessagesLocked();
} else {
// ... 其他处理逻辑
}
}
}
```