一句话说透Android里面的Looper、Handler、线程间的关系

252 阅读3分钟

一句话总结

Looper 是线程的“快递员”,负责从“包裹站”(消息队列)里不断取包裹(消息)给 Handler 处理。
Handler 是“收发室”,既能发消息到其他线程的包裹站,也能处理自己线程的包裹。
线程 是“快递员的工作区域”,主线程(UI 线程)自带快递员,子线程需要自己雇一个。


1. 三者的核心关系

角色比喻职责
线程快递员的“工作区域”执行任务的场所(主线程处理 UI,子线程处理耗时任务)。
Looper快递员不断从“包裹站”(消息队列)里取包裹(消息),并分发给对应的 Handler。
Handler收发室发送消息到其他线程的包裹站,或处理本线程的包裹(如更新 UI)。
MessageQueue包裹站按顺序存放消息的队列,Looper 会依次取出处理。

2. 主线程的默认配置

  • 主线程(UI 线程)

    • 天生自带一个 Looper(快递员)和一个 MessageQueue(包裹站)。
    • 所有 UI 操作必须在这里处理,所以系统自动配置好了“快递员”。
    • 开发者可以直接在主线程创建 Handler(收发室),无需手动初始化 Looper。

代码示例

// 主线程直接创建 Handler(默认绑定主线程的 Looper)
val mainHandler = Handler(Looper.getMainLooper())
mainHandler.post { updateUI() } // 发消息到主线程的包裹站

3. 子线程如何工作?

  • 子线程默认没有快递员
    如果想让子线程处理消息,必须手动雇一个“快递员”(Looper)并启动包裹站:

    thread(name = "子线程") {
        // 1. 雇一个快递员(初始化 Looper 和包裹站)
        Looper.prepare()
        
        // 2. 创建收发室(Handler),绑定子线程的快递员
        val subHandler = Handler(Looper.myLooper()!!) { msg ->
            Log.d("子线程", "处理消息:${msg.what}")
            true
        }
        
        // 3. 快递员开始工作(循环取包裹)
        Looper.loop()
    }
    
  • 子线程发消息到主线程

    // 子线程中发送消息到主线程的包裹站
    thread {
        val msg = Message.obtain().apply { what = 1 }
        mainHandler.sendMessage(msg) // 主线程的 Handler 处理消息
    }
    

4. 为什么主线程的 Looper 不会卡死?

  • 快递员会偷懒
    当包裹站没有包裹时,快递员(Looper)会睡觉(线程休眠),不占用 CPU。
  • 有包裹时才干活
    新包裹到达时(如用户点击、定时任务),系统会叫醒快递员处理,处理完继续睡觉。
  • 主线程的职责
    处理 UI 渲染、点击事件等消息,所有任务按顺序执行,保证流畅性。

5. 常见问题

Q1:子线程能直接更新 UI 吗?

  • 不能!UI 操作必须在主线程处理。

  • 解决:通过主线程的 Handler 发送消息:

    // 子线程中更新 UI
    thread {
        // 耗时操作...
        mainHandler.post { textView.text = "更新成功" } // 通过主线程的 Handler
    }
    

Q2:Handler 导致内存泄漏怎么办?

  • 原因:Handler 持有 Activity 引用,若 Activity 销毁但消息未处理完,会导致泄漏。

  • 解决

    • 用静态内部类 + 弱引用。

    • 在 Activity 销毁时移除所有消息:

      override fun onDestroy() {
          handler.removeCallbacksAndMessages(null)
          super.onDestroy()
      }
      

总结

  • Looper:线程的“快递员”,没有它,线程不会处理消息。

  • Handler:线程的“收发室”,负责发消息和处理消息。

  • 线程:主线程自带快递员,子线程需要自己雇一个。

  • 口诀

    • 主线程快递员已配齐,子线程需手动雇人跑腿
    • Handler 跨线程发消息,主线程 UI 更新要牢记