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

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️⃣ 消息循环的工作流程

  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。
相关推荐
千里马学框架3 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台3 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
大蓝头3 天前
安装包UI美化之路-nsNiuniuSkin全新UI调试与预览方法
ui·electron·安装包·截图控件·截屏控件
AFinalStone3 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc3 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo3 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077003 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
慧都小项3 天前
当边缘AI上产线:QtitanDocking 如何让工业 HMI 实现多视图协同
人工智能·qt·ui·边缘计算·qt6.3
其实防守也摸鱼3 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone3 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui