【Android之路】UI消息循环机制

1️⃣ 为什么需要消息循环

  • Android UI 操作必须在主线程进行 (也叫 UI ThreadMain Thread)。
  • 主线程既要响应系统事件(触摸、按键、窗口绘制)又要处理应用发送过来的任务。
  • 为了不阻塞,也为了让事件按顺序执行,Android 在主线程内部建立了一个 消息循环(Message Queue + Looper)

简化流程:

复制代码
事件(触摸/绘制/网络回调等)
        ↓
   Message
        ↓
 MessageQueue(消息队列)
        ↓
   Looper.loop()
        ↓
 Handler.dispatchMessage() → 执行回调更新UI

2️⃣ 核心组件

组件 作用
Looper 消息循环的"泵",不断从队列取消息并派发。每个线程最多只能有一个 Looper
MessageQueue 存放等待处理的 Message,按时间顺序维护。
Message 一条消息,包含 whatobjarg1/arg2target(对应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️⃣ 消息循环的工作流程

  1. 消息入队

    • 任何线程都可以通过 Handler 调用 sendMessage()post(Runnable)UI线程的 MessageQueue 投递消息。
  2. Looper.loop() 不断取消息

    • 如果队列空,线程进入休眠;有消息时立即唤醒。
  3. 分发消息

    • Looper 把消息交给它的 target(也就是创建消息的 Handler)。
  4. Handler.dispatchMessage()

    • 如果消息是 Runnable(post),执行 Runnable;否则回调 handleMessage()

示例代码

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。
相关推荐
阿巴斯甜5 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker6 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95277 小时前
Andorid Google 登录接入文档
android
黄林晴8 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab20 小时前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android