首先在UI线程创建了一个Handler实例对象,无论是匿名内部类还是自定义类生成的Handler实例对象,我们都需 要对handleMessage方法进行重写,在handleMessage方法中我们可以通过参数msg来写接受消息过后UI线程的逻辑处理,接着我们创建子线程,在子线程中需要更新UI的时候,新建一个Message对象,并且将消息的数据记录在这个消息对象Message的内部,比如arg1,arg2,obj等,然后通过前面的Handler实例对象调用sendMessge方法把这Message实例对象发送出去,之后这个消息会被存放于MessageQueue中等待被处理,此时MessageQueue的管家Looper正在不停的把MessageQueue存在的消息取出来,通过回调dispatchMessage方法将消息传递给Handler的handleMessage方法,最终前面提到的消息会被Looper从MessageQueue中取出来传递给handleMessage方法。
作者:循环不息优化不止
链接:https://juejin.cn/post/7549637439360663578
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
1. HandlerThread 是什么?
普通 Thread 默认没有 Looper,所以不能直接配合 Handler 持续接收消息。
而 HandlerThread 本质上是:
Thread + Looper + MessageQueue
它启动后,内部会创建一个 Looper,然后不断从 MessageQueue 中取消息执行。
简单理解:
HandlerThread是一个可以用Handler投递任务的后台线程。
2. 基本用法
Java 示例
HandlerThread handlerThread = new HandlerThread("work-thread");
handlerThread.start();
Handler workerHandler = new Handler(handlerThread.getLooper());
workerHandler.post(new Runnable() {
@Override
public void run() {
// 这里运行在 HandlerThread 子线程中
// 可以执行耗时操作
doSomething();
}
});
停止线程:
handlerThread.quitSafely();
3. Kotlin 示例
val handlerThread = HandlerThread("work-thread")
handlerThread.start()
val workerHandler = Handler(handlerThread.looper)
workerHandler.post {
// 这里是子线程
doSomething()
}
停止:
handlerThread.quitSafely()
4. 为什么要用 HandlerThread?
1)避免阻塞主线程
Android 主线程负责:
- UI 绘制
- 用户点击响应
- Activity 生命周期
- View 更新
如果在主线程做耗时操作,比如:
Thread.sleep(3000);
readLargeFile();
decodeBitmap();
就可能造成:
- 界面卡顿
- ANR
- 用户点击无响应
所以需要把耗时任务放到子线程。
2)比普通 Thread 更适合处理多个任务
普通线程通常这样用:
new Thread(() -> {
doSomething();
}).start();
问题是:
每来一个任务就创建一个线程,成本较高,而且不好管理。
HandlerThread 可以复用同一个线程:
workerHandler.post(task1);
workerHandler.post(task2);
workerHandler.post(task3);
这些任务会在同一个后台线程中按顺序执行。
3)天然支持消息队列
HandlerThread 里面有 MessageQueue。
所以可以:
workerHandler.postDelayed(() -> {
doSomething();
}, 1000);
也可以发送消息:
Message msg = workerHandler.obtainMessage();
msg.what = 1;
workerHandler.sendMessage(msg);
这比普通线程更适合做异步消息处理。
5. HandlerThread 的工作原理
大概流程是:
HandlerThread thread = new HandlerThread("test");
thread.start();
调用 start() 后,内部会执行它的 run() 方法:
@Override
public void run() {
Looper.prepare();
mLooper = Looper.myLooper();
Looper.loop();
}
核心就是三步:
Looper.prepare(); // 创建 Looper 和 MessageQueue
Looper.loop(); // 开启消息循环
然后你创建 Handler:
Handler handler = new Handler(handlerThread.getLooper());
这个 Handler 就绑定到了 HandlerThread 的 Looper。
之后:
handler.post(runnable);
任务会进入这个线程的消息队列,然后在 HandlerThread 中执行。
6. 为什么不能直接在普通 Thread 里 new Handler?
比如:
new Thread(() -> {
Handler handler = new Handler();
}).start();
很多时候会报错:
Can't create handler inside thread that has not called Looper.prepare()
原因是:
普通子线程默认没有 Looper。
如果你硬要用普通线程,也可以这样:
new Thread(() -> {
Looper.prepare();
Handler handler = new Handler();
Looper.loop();
}).start();
但这样写麻烦,而且需要自己管理生命周期。
所以 Android 提供了 HandlerThread 帮你封装好。
7. HandlerThread 和主线程 Handler 的区别
主线程本身就有 Looper,所以可以直接:
Handler mainHandler = new Handler(Looper.getMainLooper());
这个 Handler 执行任务在主线程。
而 HandlerThread 是子线程,需要先:
handlerThread.start();
然后再获取:
handlerThread.getLooper();
区别:
| 类型 | 执行线程 | 用途 |
|---|---|---|
Handler(Looper.getMainLooper()) |
主线程 | 更新 UI |
HandlerThread + Handler |
子线程 | 执行后台任务 |
8. 典型使用场景
HandlerThread 适合:
- 串行执行后台任务
- 文件读写
- 数据库操作
- 图片解码
- 蓝牙、相机、音视频处理
- 需要延迟执行的后台任务
- 不想频繁创建线程的场景
例如相机处理中经常会用:
HandlerThread cameraThread = new HandlerThread("CameraThread");
cameraThread.start();
Handler cameraHandler = new Handler(cameraThread.getLooper());
这样相机相关操作就在后台线程执行,不会卡 UI。
9. 注意事项
1)用完要退出
否则线程会一直存活。
handlerThread.quitSafely();
通常在 onDestroy() 里调用:
@Override
protected void onDestroy() {
super.onDestroy();
handlerThread.quitSafely();
}
2)不能在子线程直接更新 UI
错误:
workerHandler.post(() -> {
textView.setText("完成");
});
因为这段代码运行在子线程。
正确方式:
Handler mainHandler = new Handler(Looper.getMainLooper());
workerHandler.post(() -> {
String result = doSomething();
mainHandler.post(() -> {
textView.setText(result);
});
});
3)任务是串行执行的
HandlerThread 只有一个线程,所以任务会一个一个执行:
task1 -> task2 -> task3
如果你需要多个任务并发执行,应该考虑:
ThreadPoolExecutor- Kotlin Coroutine
- RxJava
- WorkManager
10. 简单总结
HandlerThread 的作用是:
创建一个带有消息循环的子线程,让我们可以用 Handler 向这个子线程发送任务。
为什么要用它?
因为:
- 避免主线程卡顿;
- 可以复用一个后台线程;
- 任务按顺序执行;
- 支持延迟任务和消息机制;
- 比自己手写
Thread + Looper更方便。
一句话:
HandlerThread就是 Android 帮你封装好的「带消息队列的后台线程」。