【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。
相关推荐
zh_xuan3 小时前
Android android.util.LruCache源码阅读
android·源码阅读·lrucache
梦终剧3 小时前
【Android之路】安卓资源与编译初步
android
会一点设计4 小时前
怎么使用AI在线识别字体?更好用的AI字体识别工具推荐
人工智能·ui·ux
诗仙&李白5 小时前
HEFrame.WpfUI :一个现代化的 开源 WPF UI库
ui·开源·wpf
mykrecording5 小时前
launch Activity流程
android·app
不知名的前端专家6 小时前
uniapp 安卓FTP上传下载操作原生插件
android·uni-app
ClassOps7 小时前
Android Studio Logcat中 杀死应用
android·android studio·logcat
lagelangri6667 小时前
MySql的存储过程以及JDBC实战
android·数据库·mysql
指针不南7 小时前
Android Studio | 设置国内代理(SDK 设置国内代理(阿里云镜像))
android·阿里云·android studio