【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。
相关推荐
Kapaseker19 小时前
你不看会后悔的2025年终总结
android·kotlin
alexhilton1 天前
务实的模块化:连接模块(wiring modules)的妙用
android·kotlin·android jetpack
航Hang*1 天前
Photoshop 图形与图像处理技术——第8章:图像的色彩与色彩调整和图像的输出与优化
图像处理·笔记·ui·photoshop
聪明努力的积极向上1 天前
【C#】线程解析:从“页面未响应”到彻底理解 .NET 中的 UI 线程、Task、Thread、COM 与消息泵
ui·.net
ji_shuke1 天前
opencv-mobile 和 ncnn-android 环境配置
android·前端·javascript·人工智能·opencv
sunnyday04261 天前
Spring Boot 项目中使用 Dynamic Datasource 实现多数据源管理
android·spring boot·后端
幽络源小助理1 天前
下载安装AndroidStudio配置Gradle运行第一个kotlin程序
android·开发语言·kotlin
inBuilder低代码平台1 天前
浅谈安卓Webview从初级到高级应用
android·java·webview
豌豆学姐1 天前
Sora2 短剧视频创作中如何保持人物一致性?角色创建接口教程
android·java·aigc·php·音视频·uniapp
白熊小北极1 天前
Android Jetpack Compose折叠屏感知与适配
android