关于Handler你不知道的事

提到Handler,我们都会想到可以用来在子线程给UI线程发送消息,常用来子线程刷新UI。
而往深了问,你一定还知道Handler会绑定到一个Looper,而每个Looper会和一个MessageQ关联,从而达到向指定线程发送消息的功能。除此之外,还了解多少?
提到MessageQueue,就不得不聊到Android的消息处理机制。

Handler消息处理机制

Android的消息机制,涉及到HandlerLooperMessageMessageQueue

  • Handler是用于处理Message的对象。它通常与一个特定的线程(通常是主线程)关联。通过Handler开发者可以将Message发送到与其关联的线程的消息队列中,以便在那个线程中执行处理。
  • Message是一个包含要传递的数据和指令的的对象。当需要在不同线程之间传递数据或执行行任务时,通常会创建一个Message并将其发送给Handler
  • Looper是一个用于管理线程的消息队列别的对象。每个线程都可以有一个Looper,它会在线程上创建一个消息队列,允许该线程接收并处理Message。主线程通常已经具有一个默认的Looper,而其他线程需要显式创建一个Looper
  • MessageQueue即是消息队列。是一个FIFO(先进先出)队列,用于存储待处理的Message。每个Looper都有一个关联的MessageQueueHandlerMessage发送到这个队列中,然后由Looper依次处理队列中的Message

简易的消息处理机制流程图

DeveloperLooperHandlerMessageQueueJNILooper@prepare循环取消息Handler@dispatchMessageLooper@loopMessage@nextnativePollOncenative处理MessageQ及LooperMessage@CallbackHandlerHandlerMessage@recycerUncheckedMessage@obtainHandler@enqueueMessageMessageQueue@enqueueMessagenativeWakeDeveloperLooperHandlerMessageQueueJNI

MessageQueue

从上面的描述,我们可以MessageQueue会按照一定的规则取出要执行的Message,当消息执行完之后Message就会被回收。

那么下面我们就通过代码来验证几种情况:

  • Message#Obtain创建消息的消息和直接New Message的方法创建消息,是否都会被回收
  • 如果消息对象被持有,是否消息也会被回收
  • 如果接受消息在主线程,那么在View#post方法执行后,消息是否会被回收
  • 如果接收消息在主线程,那么当切换到其他线程执行任务后,消息是否会被回收
  • 如果接受消息在主线程,通过Kotlin Coroutine执行任务在 Dispatchers.MainDispatchers.Default后,消息是否会被回收

通过Message#Obtain创建消息并发送

 Message.obtain(). also { it.what = 1  mObtainMsg = it mHandler.sendMessage(it) } 

通过直接New的方法创建消息并发送

Message().also {
it.what = 2
    mHandler.sendMessage(it)
} 

发送消息后打印消息信息

class MyHandler(private val actRef: WeakReference<TestMessageActivity>) :
    Handler(Looper.getMainLooper()) {
    override fun handleMessage(msg: Message) {
        super.handleMessage(msg)
        val act = actRef.get() ?: return
        Log.d(TAG, "msg:$msg}")
        Log.d(TAG, "mObtainMsg:${act.mObtainMsg}}")

        act.mSWThread.post {
Log.d(TAG, "msg--post:$msg}")
            Log.d(TAG, "mObtainMsg--post:${act.mObtainMsg}}")
        }
if (act.mSWThread.isChecked) {//是否开启了子线程
            Thread {
Log.d(TAG, "thread--msg:$msg}")
                Log.d(TAG, "thread--mObtainMsg:${act.mObtainMsg}}")
                Thread.sleep(3000L)
                Log.d(TAG, "threadAfter--msg:$msg}")
                Log.d(TAG, "threadAfter--mObtainMsg:${act.mObtainMsg}}")
            } .start()
        }
        if (act.mSWCoroutine.isChecked) {//是否开启协程
            GlobalScope.launch(Dispatchers.Main) {
Log.d(TAG, "launchMain----msg:$msg}")
                Log.d(TAG, "launchMain--mObtainMsg:${act.mObtainMsg}}")
            }
GlobalScope.launch {
Log.d(TAG, "launch--msg:$msg}")
                Log.d(TAG, "launch--mObtainMsg:${act.mObtainMsg}}")
            }
}


    }
}

Obtain创建消息发送消息,收到的日志结果

2024-03-22 16:32:04.780 21621-21621/com.lotus.duode.myapplication D/TestMessageActivity: msg:{ when=-1ms what=1 target=com.lotus.duode.myapplication.TestMessageActivity$MyHandler }}
2024-03-22 16:32:04.780 21621-21621/com.lotus.duode.myapplication D/TestMessageActivity: mObtainMsg:{ when=-1ms what=1 target=com.lotus.duode.myapplication.TestMessageActivity$MyHandler }}
2024-03-22 16:32:04.781 21621-22106/com.lotus.duode.myapplication D/TestMessageActivity: thread--msg:{ when=-2ms what=1 target=com.lotus.duode.myapplication.TestMessageActivity$MyHandler }}
2024-03-22 16:32:04.781 21621-22106/com.lotus.duode.myapplication D/TestMessageActivity: thread--mObtainMsg:{ when=-2ms what=1 target=com.lotus.duode.myapplication.TestMessageActivity$MyHandler }}
2024-03-22 16:32:04.803 21621-22108/com.lotus.duode.myapplication D/TestMessageActivity: launch--msg:{ when=-19ms callback=android.view.Choreographer$FrameDisplayEventReceiver target=android.view.Choreographer$FrameHandler }}
2024-03-22 16:32:04.803 21621-22108/com.lotus.duode.myapplication D/TestMessageActivity: launch--mObtainMsg:{ when=-19ms callback=android.view.Choreographer$FrameDisplayEventReceiver target=android.view.Choreographer$FrameHandler }}
2024-03-22 16:32:04.806 21621-21621/com.lotus.duode.myapplication D/TestMessageActivity: msg--post:{ when=-2d2h37m50s923ms barrier=0 }}
2024-03-22 16:32:04.806 21621-21621/com.lotus.duode.myapplication D/TestMessageActivity: mObtainMsg--post:{ when=-2d2h37m50s923ms barrier=0 }}
2024-03-22 16:32:04.806 21621-21621/com.lotus.duode.myapplication D/TestMessageActivity: launchMain----msg:{ when=-2d2h37m50s923ms barrier=0 }}
2024-03-22 16:32:04.806 21621-21621/com.lotus.duode.myapplication D/TestMessageActivity: launchMain--mObtainMsg:{ when=-2d2h37m50s923ms barrier=0 }}
2024-03-22 16:32:07.782 21621-22106/com.lotus.duode.myapplication D/TestMessageActivity: threadAfter--msg:{ when=-2d2h37m53s899ms barrier=0 }}
2024-03-22 16:32:07.782 21621-22106/com.lotus.duode.myapplication D/TestMessageActivity: threadAfter--mObtainMsg:{ when=-2d2h37m53s899ms barrier=0 }}

New Message创建消息发送消息,收到的日志结果

2024-03-22 16:32:27.427 21621-21621/com.lotus.duode.myapplication D/TestMessageActivity: msg:{ when=-9ms what=2 target=com.lotus.duode.myapplication.TestMessageActivity$MyHandler }}
2024-03-22 16:32:27.428 21621-21621/com.lotus.duode.myapplication D/TestMessageActivity: mObtainMsg:{ when=-2ms callback=android.view.Choreographer$FrameDisplayEventReceiver target=android.view.Choreographer$FrameHandler }}
2024-03-22 16:32:27.428 21621-22116/com.lotus.duode.myapplication D/TestMessageActivity: thread--msg:{ when=-10ms what=2 target=com.lotus.duode.myapplication.TestMessageActivity$MyHandler }}
2024-03-22 16:32:27.428 21621-22116/com.lotus.duode.myapplication D/TestMessageActivity: thread--mObtainMsg:{ when=-3ms callback=android.view.Choreographer$FrameDisplayEventReceiver target=android.view.Choreographer$FrameHandler }}
2024-03-22 16:32:27.428 21621-22108/com.lotus.duode.myapplication D/TestMessageActivity: launch--msg:{ when=-2d2h38m13s545ms barrier=0 }}
2024-03-22 16:32:27.428 21621-22108/com.lotus.duode.myapplication D/TestMessageActivity: launch--mObtainMsg:{ when=-3ms callback=android.view.Choreographer$FrameDisplayEventReceiver target=android.view.Choreographer$FrameHandler }}
2024-03-22 16:32:27.430 21621-21621/com.lotus.duode.myapplication D/TestMessageActivity: msg--post:{ when=-1ms callback=android.view.Choreographer$$ExternalSyntheticLambda0 target=android.view.Choreographer$FrameHandler }}
2024-03-22 16:32:27.430 21621-21621/com.lotus.duode.myapplication D/TestMessageActivity: mObtainMsg--post:{ when=-2d2h38m13s547ms barrier=0 }}
2024-03-22 16:32:27.430 21621-21621/com.lotus.duode.myapplication D/TestMessageActivity: launchMain----msg:{ when=-2ms callback=android.view.Choreographer$$ExternalSyntheticLambda0 target=android.view.Choreographer$FrameHandler }}
2024-03-22 16:32:27.430 21621-21621/com.lotus.duode.myapplication D/TestMessageActivity: launchMain--mObtainMsg:{ when=-2d2h38m13s547ms barrier=0 }}
2024-03-22 16:32:30.429 21621-22116/com.lotus.duode.myapplication D/TestMessageActivity: threadAfter--msg:{ when=-2d2h38m16s546ms barrier=0 }}
2024-03-22 16:32:30.429 21621-22116/com.lotus.duode.myapplication D/TestMessageActivity: threadAfter--mObtainMsg:{ when=-2d2h38m16s546ms barrier=0 }}

结论

通过Handler#handleMessage接收的消息,在消息执行完成之后。如果后续有新的消息需要被使用,那么系统会通过Message#obtain创建消息时,就会从Message#sPool 中进行复用

请添加图片描述

而消息的回收是在DispatchMessage完成之后。

在这里插入图片描述

因此如果我们需要持久化使用Message中的信息时,就需要使用变量对这些信息进行存储。

在这里插入图片描述

<think>好的,用户想了解Android中的Handler消息处理机制,包括原理和使用教程。我需要根据提供的三个引用资料来整理回答,确保内容准确,并且符合用户的要求,比如使用正确的中文、LaTeX格式,生成相关问题等等。 首先,我需要回顾一下Handler的基本作用。根据引用[2],Handler的主要作用是将任务切换到其所在的线程执行,尤其是在子线程操作后更新UI,因为Android允许子线程直接访问UI。所以,Handler常用于线程间通信,特别是子线程到主线程的消息传递。 接下来,关于原理部分,引用[1]和[3]提到消息机制的核心包括Handler、MessageQueue、Looper和Message。需要详细解释这四个组件的关系。Looper负责循环取出消息,MessageQueue存储消息队列,Handler发送和处理消息。可能还需要提到Looper.prepare()和Looper.loop()的初始化过程,以及主线程中已经创建好的Looper。 在消息传递流程方面,用户可能想知道消息是如何从发送到处理的。例如,Handler发送消息到MessageQueue,Looper断取出消息并回调给Handler的handleMessage方法。这里需要分步骤说明,确保流程清晰。 使用教程部分,需要给出创建Handler、发送消息和处理消息的具体代码示例。比如在主线程创建Handler,在子线程使用post或sendMessage方法发送消息,然后在handleMessage中更新UI。同时要注意代码的正确性,比如在子线程中创建Handler需要先调用Looper.prepare(),但主线程已经默认有了Looper,所以需要。 然后,根据用户的要求生成相关问题。可能需要包括Handler的其他用途、避免内存泄漏的方法、主线程为何需要手动创建Looper等。这些问题需要覆盖原理、使用注意项和常见问题。 最后,检查是否符合所有系统级指令,比如行内数学表达式是否用$...$,独立公式是否用$$...$$,过当前内容可能涉及数学公式,但需要确认是否有其他格式要求。确保LaTeX语法正确,用中文回答,结构清晰,分步骤说明,并在引用的地方添加引用标识。 可能需要注意引用资料的来源是否准确,特别是引用的三个资料都是关于Handler的,需要正确对应到各个部分。例如,引用[3]中的handleCallback方法可能涉及消息处理的内部机制,可以提到Message的callback运行。 同时,用户可能存在的深层需求是了解如何正确使用Handler避免ANR或内存泄漏,或者理解其背后的设计模式,比如消息循环模型。在回答中可以适当提及这些点,但根据提供的引用资料,可能主要依据现有内容。 现在需要整理这些思路,按照用户提供的结构来组织回答,确保每个部分都有对应的引用支持,并且问题部分相关且合理。</think>### Android Handler消息处理机制详解 #### 一、核心组件与原理 AndroidHandler消息机制基于四个核心组件: 1. **Handler**:负责发送消息到`MessageQueue`,并处理`Looper`分发的消息。 2. **MessageQueue**:以队列形式存储消息(单链表实现)[^1][^3]。 3. **Looper**:循环从`MessageQueue`中取出消息并分发给对应的`Handler`[^2]。 4. **Message**:封装消息内容,包含目标`Handler`和回调方法等信息。 **运行流程**: 1. **Looper初始化**:主线程默认已初始化`Looper`,子线程需手动调用`Looper.prepare()`和`Looper.loop()`[^2]。 2. **消息发送**:通过`Handler.sendMessage()`或`post(Runnable)`将消息加入队列。 3. **消息循环**:`Looper`通过`MessageQueue.next()`轮询取出消息,通过`msg.target.dispatchMessage(msg)`回调处理。 4. **消息处理**:根据消息类型执行: - 若`Message`设置了`callback`(如`Runnable`),直接执行`message.callback.run()`。 - 否则,调用`Handler.handleMessage()`实现业务逻辑。 #### 二、使用教程 **1. 主线程创建Handler(更新UI)** ```java // 主线程中创建Handler Handler handler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == 1) { textView.setText("更新UI成功"); } } }; // 子线程发送消息 new Thread(() -> { Message msg = handler.obtainMessage(1); handler.sendMessage(msg); }).start(); ``` **2. 子线程创建Handler(需初始化Looper)** ```java new Thread(() -> { Looper.prepare(); // 初始化Looper Handler subHandler = new Handler() { @Override public void handleMessage(Message msg) { // 处理子线程消息 } }; Looper.loop(); // 启动消息循环 }).start(); ``` **3. 发送消息的两种方式** - **延时消息**:`handler.sendMessageDelayed(msg, 1000)` - **Runnable方式**:`handler.post(() -> { /* 执行代码 */ })` #### 三、关键注意项 1. **内存泄漏**:若`Handler`持有`Activity`引用,需使用`WeakReference`或`static`修饰[^1]。 2. **主线程限制**:`handleMessage()`默认在主线程执行,避免耗时操作(否则可能引发ANR)[^2]。 3. **Looper生命周期**:子线程使用完需调用`Looper.quit()`释放资源。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值