【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。
相关推荐
赏金术士14 小时前
Kotlin 习题集 · 高级篇
android·开发语言·kotlin
问心无愧051316 小时前
ctf show web 入门42
android·前端·android studio
Soari17 小时前
字节跳动重磅开源:UI-TARS-desktop 深度拆解,构建跨平台的“全自动”多模态 AI Agent
人工智能·ui
没什么本事17 小时前
关于C# panel 添加lable问题 -- 明确X和Y 位置错误
android·java·c#
jf加菲猫17 小时前
第21章 Qt WebEngine
开发语言·c++·qt·ui
REDcker21 小时前
Android HWASan 详解:硬件标记原理、Clang 启用与排障实践
android·linux·debug·编译·clang·asan·hwasan
2501_9159090621 小时前
全面解析前端开发中常用的浏览器调试工具及其使用场景
android·ios·小程序·https·uni-app·iphone·webview
angerdream21 小时前
Android手把手编写儿童手机远程监控App之SQLite详解2
android
-SOLO-21 小时前
Python 爬取小红书 文章标题和内容 仅供学习
android·python·学习
ooseabiscuit1 天前
Laravel5
android·php·laravel