��于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

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

sequenceDiagram Developer->>Looper: Looper@prepare Looper ->> Looper: 循环取消息 Looper->>Handler:Handler@dispatchMessage Looper->>MessageQueue: Looper@loop MessageQueue->> MessageQueue: Message@next MessageQueue->>JNI:nativePollOnce Note Over JNI:native处理MessageQ及Looper Handler-->>Developer:Message@Callback Handler-->>Developer:Handler#Callback Handler-->>Developer:Handler#handleMessage Looper->>MessageQueue:Message@recycerUnchecked Developer->>Handler:Message@obtain Handler->>Handler:Handler@enqueueMessage Handler->>MessageQueue: MessageQueue-->>MessageQueue:MessageQueue@enqueueMessage MessageQueue ->> JNI:nativeWake

MessageQueue

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

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

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

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

ini 复制代码
 Message.obtain(). also { it.what = 1  mObtainMsg = it mHandler.sendMessage(it) } 

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

scss 复制代码
Message().also {
it.what = 2
    mHandler.sendMessage(it)
} 

发送消息后打印消息信息

kotlin 复制代码
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创建消息发送消息,收到的日志结果

ini 复制代码
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创建消息发送消息,收到的日志结果

ini 复制代码
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中的信息时,就需要使用变量对这些信息进行存储。

相关推荐
转世成为计算机大神17 分钟前
易考八股文之Java中的设计模式?
java·开发语言·设计模式
qq_3273427338 分钟前
Java实现离线身份证号码OCR识别
java·开发语言
长亭外的少年1 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
阿龟在奔跑2 小时前
引用类型的局部变量线程安全问题分析——以多线程对方法局部变量List类型对象实例的add、remove操作为例
java·jvm·安全·list
飞滕人生TYF2 小时前
m个数 生成n个数的所有组合 详解
java·递归
代码小鑫2 小时前
A043-基于Spring Boot的秒杀系统设计与实现
java·开发语言·数据库·spring boot·后端·spring·毕业设计
真心喜欢你吖2 小时前
SpringBoot与MongoDB深度整合及应用案例
java·spring boot·后端·mongodb·spring
激流丶2 小时前
【Kafka 实战】Kafka 如何保证消息的顺序性?
java·后端·kafka
周全全3 小时前
Spring Boot + Vue 基于 RSA 的用户身份认证加密机制实现
java·vue.js·spring boot·安全·php
uzong3 小时前
一个 IDEA 老鸟的 DEBUG 私货之多线程调试
java·后端