1️⃣ 为什么需要消息循环
- Android UI 操作必须在主线程进行 (也叫 UI Thread 或 Main Thread)。
- 主线程既要响应系统事件(触摸、按键、窗口绘制)又要处理应用发送过来的任务。
- 为了不阻塞,也为了让事件按顺序执行,Android 在主线程内部建立了一个 消息循环(Message Queue + Looper)。
简化流程:
事件(触摸/绘制/网络回调等)
↓
Message
↓
MessageQueue(消息队列)
↓
Looper.loop()
↓
Handler.dispatchMessage() → 执行回调更新UI
2️⃣ 核心组件
组件 | 作用 |
---|---|
Looper | 消息循环的"泵",不断从队列取消息并派发。每个线程最多只能有一个 Looper 。 |
MessageQueue | 存放等待处理的 Message ,按时间顺序维护。 |
Message | 一条消息,包含 what 、obj 、arg1/arg2 、target (对应Handler)。 |
Handler | 发送消息(sendMessage )到队列,处理消息(handleMessage )。 |
主线程的
Looper
在应用启动时由系统自动创建并启动。
🔥 主线程启动 Looper 的源码片段(简化版)
ActivityThread.main()
:
java
public static void main(String[] args) {
Looper.prepareMainLooper(); // 创建主线程 Looper 和 MessageQueue
ActivityThread thread = new ActivityThread();
thread.attach(false);
Looper.loop(); // 开始轮询消息
}
3️⃣ 消息循环的工作流程
-
消息入队
- 任何线程都可以通过
Handler
调用sendMessage()
、post(Runnable)
向 UI线程的 MessageQueue 投递消息。
- 任何线程都可以通过
-
Looper.loop() 不断取消息
- 如果队列空,线程进入休眠;有消息时立即唤醒。
-
分发消息
- Looper 把消息交给它的
target
(也就是创建消息的Handler
)。
- Looper 把消息交给它的
-
Handler.dispatchMessage()
- 如果消息是 Runnable(
post
),执行 Runnable;否则回调handleMessage()
。
- 如果消息是 Runnable(
示例代码
kotlin
class MainActivity : AppCompatActivity() {
private val handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
if (msg.what == 1) {
textView.text = "收到消息: ${msg.obj}"
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Thread {
val msg = Message.obtain()
msg.what = 1
msg.obj = "Hello from background thread"
handler.sendMessage(msg)
}.start()
}
}
解析:
- 子线程创建 Message 并
sendMessage()
。- 主线程的 Looper.loop() 会取出消息并交给
handler.handleMessage()
,安全更新 UI。
4️⃣ 常用操作模式
🔹 1. post(Runnable)
kotlin
handler.post {
textView.text = "更新UI"
}
把任务直接投递给主线程执行,避免手动写 Message
。
🔹 2. 延时执行
kotlin
handler.postDelayed({
textView.text = "2秒后更新UI"
}, 2000)
🔹 3. 在子线程创建自己的 Looper
kotlin
Thread {
Looper.prepare() // 当前线程创建 Looper 和 MessageQueue
val handler = Handler(Looper.myLooper()!!) {
// 处理子线程消息
true
}
Looper.loop()
}.start()
5️⃣ 与 ANR 的关系
- 主线程的 Looper 如果被长时间阻塞(如做了大量耗时计算或网络请求),就无法处理输入/绘制消息。
- 超过 5s(前台 Activity)/10s(后台 Service)不响应 → ANR(应用无响应)。
- 所以耗时操作一定要放到子线程,通过 Handler/LiveData/Coroutine 切回主线程更新 UI。
6️⃣ Handler 与其他线程切换方式对比
技术 | 本质 | 适用场景 |
---|---|---|
Handler | 基于 Looper 消息机制 | 最经典,适合需要精确消息控制 |
runOnUiThread | 主线程 Handler 封装 | 快速回到主线程更新 UI |
AsyncTask (废弃) | 封装线程+Handler | 轻量异步任务 |
HandlerThread | 内置 Looper 的线程 | 创建带消息循环的工作线程 |
Kotlin Coroutine | 调度器切换 | 现代异步,更简洁 |
🧩 总结
Android UI 消息循环 是通过
Looper + MessageQueue + Handler
实现的:
- 主线程启动时就有一个 Looper。
- 消息 从各个线程发到
MessageQueue
。Looper.loop()
不断取出消息交给 Handler 分发,最终更新 UI。- 如果主线程被阻塞,UI 无法刷新,会触发 ANR。