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

相关推荐
杨半仙儿还未成仙儿2 分钟前
java注解
java·开发语言
我是Superman丶9 分钟前
【工具】Java Excel转图片
java·python·excel
wxin_VXbishe12 分钟前
springboot瑜伽课约课小程序-计算机毕业设计源码87936
java·c++·spring boot·python·spring·servlet·php
会有黎明吗30 分钟前
完整版订单超时自动取消功能
java·vue·rabbitmq
1.01^100035 分钟前
[000-01-008].第05节:OpenFeign特性-重试机制
java·开发语言
小扳36 分钟前
Redis 篇-初步了解 Redis 持久化、Redis 主从集群、Redis 哨兵集群、Redis 分片集群
java·spring boot·redis·分布式·缓存
guangzhi063337 分钟前
JVM运行区域介绍
java·jvm
灯火不休ᝰ41 分钟前
7--SpringBoot-后端开发、原理
java·spring boot·spring
微刻时光1 小时前
Redis 主从复制
java·redis·笔记·github
汪公子4921 小时前
k8s的配置
java·linux·docker