持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情
一、前言
在Handler系列的第一篇我们简单的用了一个Demo展示了在子线程中定义Handler,我们需要初始化Looper,还需要调用looper.prepare和looper.loop,才能创建一个循环消息机制
其实Android内部已经有一个帮我们封装好的HandlerThread了,今天咱们就来探究一下HandlerThread的源码及使用情况
二、HandlerThread
2.1 成员变量
public class HandlerThread extends Thread {
//线程任务优先级
int mPriority;
int mTid = -1;
//当前线程的Looper对象
Looper mLooper;
//传递消息的handler
private @Nullable Handler mHandler;
...
- 可以看到HandlerThread继承自Thread类,自己是有run方法可以执行耗时任务的.
- 同时在成员变量中我们看到了Looper和Handler,都已经帮我们准备好了
2.2 构造函数
//线程名字,方便后续查找,优先级默认
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
//自定义线程名字+优先级
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
2.3 run()方法
@Override
public void run() {
mTid = Process.myTid();
//ThreadLocal,创建子线程的looper
Looper.prepare();
synchronized (this) {
//获取当前线程的looper
mLooper = Looper.myLooper();
//唤醒所有等待--下面分析
notifyAll();
}
//设置线程优先级
Process.setThreadPriority(mPriority);
//looper准备工作
onLooperPrepared();
//looper开始轮询
Looper.loop();
mTid = -1;
}
2.4 使用方法
HandlerThread handlerThread = new HandlerThread("handler-Thread");
handlerThread.start();
Handler childHandler = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
//处理其他线程发送的消息
//运行在子线程
}
};
因为我们的子线程处理消息的handler此时是在子线程,而此时handler实例化时是需要传入当前线程的looper的,所以对应的实例化是需要拿到thread的looper
2.5 getLooper()方法
public Looper getLooper() {
//判断是否存活
if (!isAlive()) {
return null;
}
boolean wasInterrupted = false;
//如果已经存活,等待looper在run()方法中创建
synchronized (this) {
while (isAlive() && mLooper == null) {
//死循环,等待
try {
//唤醒-对应run()方法里的notifyAll()
wait();
} catch (InterruptedException e) {
wasInterrupted = true;
}
}
}
if (wasInterrupted) {
Thread.currentThread().interrupt();
}
//返回looper对象
return mLooper;
}
可以看到这里返回Looper对象,首先判断线程是否存活,然后判断looper是否已经创建成功,如果looper是null,就wait等待唤醒notify,等run()方法执行完毕并创建成功.
此方法确保了在使用Handler之前已经创建了在子线程中的Looper
2.6 quit()
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;
}
可以看到HandlerThread提供了两个退出的方法,一个是直接退出,一个是安全退出.而它们的调用都是依赖looper.对应的是MessageQueue
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
- 当我们调用了
quit方法时,其实最终是调用了MessageQueue的removeAllFutureMessagesLocked方法,该方法是清空MessageQueue消息池中的所有消息,包括延迟消息和非延迟消息 - 当我们调用了
quitSafely方法时,其实是调用了removeAllMessagesLocked方法,该方法会清空MessageQueue消息池中的全部延迟消息,同时将消息池中全部的非延迟消息派发出去让Handler处理
quitSafely相比较quit方法,会派发全部的非延迟消息
三、Demo使用
接下来我们简单的用一个Demo演示一下如何使用HandlerThread
public class HandlerThreadActivity extends AppCompatActivity {
private ActivityHandlerThreadBinding binding;
private MainHandler mainHandler;
private static class MainHandler extends Handler {
private WeakReference<HandlerThreadActivity> handlerThreadActivity;
public MainHandler(HandlerThreadActivity handlerThreadActivity) {
this.handlerThreadActivity = new WeakReference<>(handlerThreadActivity);
}
@Override
public void handleMessage(@NonNull Message msg) {
//主线程的handler
super.handleMessage(msg);
String description = msg.getData().getString("data");
Log.i("data", "主线程" + description);
if (handlerThreadActivity.get() != null) {
handlerThreadActivity.get().binding.tvShow.setText(description);
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityHandlerThreadBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
mainHandler = new MainHandler(HandlerThreadActivity.this);
HandlerThread handlerThread = new HandlerThread("handler-Thread");
handlerThread.start();
//子线程接收多个msg
Handler childHandler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
//处理其他线程发送的消息
//运行在子线程
String description = msg.getData().getString("data");
Log.i("data", "子线程" + description);
Message message = mainHandler.obtainMessage();
Bundle bundle = message.getData();
bundle.putString("data", description);
//调用主线程的handler发送消息
mainHandler.sendMessage(message);
}
};
for (int i = 0; i < 10; i++) {
//子线程发送消息
Message message = childHandler.obtainMessage();
Bundle data = new Bundle();
data.putString("data", "hello world" + i);
message.setData(data);
childHandler.sendMessageDelayed(message, 1000 * i);
}
}
}
3.1 系统使用
而我们也在Android源码里看到很多耗时任务的类中都定义使用了HandlerThread
NotificationManagerCompat.java
SideChannelManager(Context context) {
mContext = context;
mHandlerThread = new HandlerThread("NotificationManagerCompat");
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper(), this);
}
3.2小结
可以看到,使用的具体步骤是
- 创建HandlerThread
- 通过start启动
- HandlerThread在run方法内部创建了Looper对象
- handler对象创建并且和looper绑定
- 在子线程的handler的handleMessage方法内,处理消息
同时需要注意的是,handlerThread内部的run()方法的looper()循环也是是一个无限循环,当我们的任务已经执行完毕后,需要及时调用quit()或者quitSafely()方法退出循环
四、总结
当我们在开发中需要频繁的开启子线程做耗时操作,通过创建多个匿名线程
new Thread(){}.start();
会使得程序变慢,当我们有耗时任务需要在子线程执行,并且需要与主线程进行一些交互时可以使用HandlerThread,注意执行完耗时任务后记得关闭looper的循环.
五、彩蛋
在上面使用handler发送消息时,不知道你是否注意到,我们在新建一个消息时没有简单的new一个消息,而是使用了
Message message = handler.obtainMessage();
对于需要处理大量消息的MessageQueue来说,它的消息池肯定是存在大量新建的消息以及被处理过的消息的,那么它是如何存储这些消息,保证消息体的复用效率的呢?
这也是下一章我们需要讨论的问题~