《Android Handler:线程间通信的核心实现》

在 Android 中,Handler 机制是一套用于线程间通信的核心机制,主要解决 "子线程无法直接更新 UI" 的问题(Android 规定 UI 操作必须在主线程执行,否则会抛出异常),同时也用于实现延迟任务、定时任务等场景。

1. Message(消息)

  • 是线程间传递的数据载体,可携带简单数据(如whatarg1arg2)或复杂对象(obj)。
  • 为了避免频繁创建对象,可通过Message.obtain()从消息池获取(复用机制),比new Message()更高效。

2. MessageQueue(消息队列)

  • 是一个单链表结构 的队列,用于存储Handler发送的Message,按消息的触发时间(when)排序(早触发的在前)。
  • 每个线程最多只有一个MessageQueue,由Looper创建并管理。

3. Looper(消息循环器)

  • 是 "消息循环" 的核心,负责不断从 MessageQueue 中取出消息 ,并分发到对应的Handler处理。
  • 每个线程最多只有一个Looper(通过ThreadLocal实现线程隔离,确保线程私有)。
  • 主线程(UI 线程)在启动时会自动初始化Looper(通过ActivityThread.main()方法),因此主线程可直接使用Handler;子线程若要使用Handler,需手动调用Looper.prepare()初始化Looper,再调用Looper.loop()启动循环。

4. Handler(处理器)

  • 负责发送消息到 MessageQueue (如sendMessage()post()等方法),并处理 Looper 分发的消息 (重写handleMessage()方法)。
  • 一个Handler关联一个Looper(创建时默认绑定当前线程的Looper),因此可通过Handler将消息从一个线程发送到另一个线程的MessageQueue

工作流程

  1. 初始化 Looper :线程启动时,若需使用 Handler 机制,需通过Looper.prepare()创建Looper和对应的MessageQueue(主线程默认初始化)。
  2. 创建 Handler :在目标线程(如主线程)创建Handler,它会自动绑定当前线程的LooperMessageQueue
  3. 发送消息 :其他线程(如子线程)通过HandlersendMessage()(发送Message)或post()(发送Runnable,内部会包装为Message)将消息加入MessageQueue
  4. 消息循环Looper.loop()启动死循环,不断从MessageQueue中取出消息(若队列空则阻塞,释放 CPU),并通过Message中关联的Handler分发消息。
  5. 处理消息Handler收到消息后,通过dispatchMessage()方法处理(优先执行MessageRunnable,其次是HandlerhandleMessage())。

关键细节

  • 线程与 Looper 的关系:1 个线程 → 1 个 Looper → 1 个 MessageQueue → N 个 Handler(多个 Handler 可绑定同一 Looper)。
  • Looper 的死循环为何不 ANR :当MessageQueue为空时,Looper会阻塞在next()方法(通过 Linux 的epoll机制休眠),不占用 CPU;有新消息时会被唤醒,因此不会导致 ANR。
  • 内存泄漏风险 :若Handler是 Activity 的非静态内部类,会持有 Activity 的强引用;若消息在队列中延迟未处理,会导致 Activity 无法被回收(内存泄漏)。解决:将Handler定义为静态内部类 ,并通过弱引用(WeakReference) 持有 Activity;在 Activity 销毁时(onDestroy())调用handler.removeCallbacksAndMessages(null)移除所有消息。

典型场景

  • 子线程执行耗时操作(如下载、数据库查询)后,通过Handler通知主线程更新 UI。
  • 实现延迟任务(如handler.postDelayed(runnable, 1000))。
  • 主线程向子线程发送指令(如停止子线程的任务)。

1.子线程→主线程通信

kotlin 复制代码
class MainActivity : AppCompatActivity() {
    companion object {
        private const val TAG = "HandlerDemo"
        private const val WHAT_UPDATE = 2
    }


    // 后台线程与其 Handler(示例使用 HandlerThread)
    private lateinit var workerThread: HandlerThread
    private lateinit var workerHandler: Handler

    // 界面元素:用于展示处理结果
    private lateinit var statusText: TextView
    private lateinit var actionButton: Button

    // Message 处理示例:在主线程中统一处理结构化消息
    private val messageHandler = object : Handler(Looper.getMainLooper()) {
        override fun handleMessage(msg: Message) {
            when (msg.what) {
                WHAT_UPDATE -> {
                    val text = "Updated: ${msg.obj}"
                    Log.d(TAG, "handleMessage WHAT_UPDATE: ${msg.obj}")
                    // 更新到界面
                    if (::statusText.isInitialized) {
                        statusText.text = text
                    }
                }
                else -> {
                    Log.d(TAG, "handleMessage what=${msg.what}")
                }
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContentView(R.layout.activity_main)
        // 绑定 TextView,用于展示主线程/后台线程的处理结果
        statusText = findViewById(R.id.statusText)
        actionButton = findViewById(R.id.actionButton)

        // 2) HandlerThread:创建后台线程并投递任务
        workerThread = HandlerThread("DemoWorker")
        workerThread.start()
        workerHandler = Handler(workerThread.looper)

        // 点击按钮后触发:仅演示子线程 -> 主线程通信
        actionButton.setOnClickListener {
            statusText.text = "Starting..."

            // 后台线程:执行任务并通过 Message 回到主线程
            workerHandler.post {
                Log.d(TAG, "workerHandler.post on ${Thread.currentThread().name}")
                Thread.sleep(300)
                val msg = messageHandler.obtainMessage(WHAT_UPDATE, "Result from worker")
                messageHandler.sendMessage(msg)
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        // 清理消息与回调,避免泄漏;安全退出后台线程
        messageHandler.removeCallbacksAndMessages(null)
        if (::workerThread.isInitialized) {
            workerThread.quitSafely()
        }
    }
}

2.实现延迟任务

kotlin 复制代码
// 后台线程:延迟启动任务,然后立即把结果发回主线程
Log.d(TAG, "schedule workerHandler.postDelayed(+5s)")
workerHandler.postDelayed({
    Log.d(TAG, "workerHandler.postDelayed on ${Thread.currentThread().name}")
    Thread.sleep(300)
    val msg = messageHandler.obtainMessage(WHAT_UPDATE, "Result from worker (delayed start)")
    messageHandler.sendMessage(msg)
}, 5000L)
相关推荐
技术小丁10 小时前
使用 HTML + JavaScript 实现酒店订房日期选择器(附完整源码)
前端·javascript
hashiqimiya10 小时前
harmonyos的鸿蒙的跳转页面的部署
开发语言·前端·javascript
向日葵同志4433010 小时前
使用@univerjs纯前端渲染excel, 显示图片、链接、样式
前端·react.js·excel
可别39010 小时前
使用Worker打包报错
前端·vue.js
GIS瞧葩菜10 小时前
【无标题】
开发语言·前端·javascript·cesium
T___T11 小时前
彻底搞懂 CSS 盒子模型 box-sizing:小白也能看懂的布局核心
前端·面试
彭于晏爱编程11 小时前
关于表单,别做工具库舔狗
前端·javascript·面试
空白格9711 小时前
Android插件化开发
前端
风中凌乱的L11 小时前
vue canvas标注
前端·vue.js·canvas
拉不动的猪11 小时前
什么是二义性,实际项目中又有哪些应用
前端·javascript·面试