关于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>好的,用户想用Kotlin协程替代Handler实现异步任务。首先,我需要回顾一下Handler的传统用法,通常是在子线程执行耗时任务,然后通过Handler.post切换到主线程更新UI。而协程可以简化这个过程,避免回调地狱和内存泄漏问题。 接下来,我需要根据提供的引用内容来整理步骤。引用[1]提到协程可以替代Handler处理线程间通信,引用[2]指出协程底层还是使用Handler的post方法切换回主线程,这说明协程在切换线程时依然依赖Handler,但封装得更好,用户无需直接操作。引用[3]和[4]比较了launch和async的区别,用户需要知道需要返回值时使用launch,需要结果时用async。引用[5]提到了Dispatchers,这涉及到协程的线程调度,比如Dispatchers.Main用于主线程,Dispatchers.IO用于子线程。 用户可能需要具体的步骤:如何启动协程,切换线程,处理异常等。需要分步骤说明,比如添加依赖、使用CoroutineScope、切换调度器。同时,要比较协程和Handler的优势,比如代码简洁、自动生命周期管理等。 还要考虑用户可能的疑问,比如协程的作用域管理,防止内存泄漏,以及如何处理取消操作。需要引用相关的内容,比如CoroutineScope和Job的使用。可能还要提到在Android中常用的viewModelScope或lifecycleScope,这些可以自动绑定生命周期,避免泄漏。 最后,生成相关问题,比如性能分析、优化方法、应用场景等,帮助用户深入理解。确保回答结构清晰,步骤明确,引用正确,比如切换线程的部分引用[2],launch和async的区别引用[3]和[4]。</think>### 如何用 Kotlin 协程替代 Handler 实现异步任务 #### 1. **协程的基本原理** Kotlin 协程通过 `CoroutineScope` 和 `Dispatchers` 实现线程切换。与 Handler 的 `post` 方法类似,协程底层切换回主线程时仍通过 `Handler.post()` 实现[^2],但语法更简洁且天然支持生命周期管理。 #### 2. **实现步骤** **步骤 1:添加协程依赖** 在项目的 `build.gradle` 中添加: ```kotlin implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4" ``` **步骤 2:启动协程** - **使用 `launch` 执行无返回值的异步任务** 适用于需要返回结果的操作(如文件操作、UI更新): ```kotlin CoroutineScope(Dispatchers.IO).launch { // 子线程执行耗时任务(如网络请求) val result = fetchData() // 切换回主线程更新 UI withContext(Dispatchers.Main) { textView.text = result // 引用[2] } } ``` - **使用 `async` 执行有返回值的异步任务** 适用于需要获取结果的场景(如数据库查询): ```kotlin val deferredResult = CoroutineScope(Dispatchers.IO).async { return@async queryDatabase() } // 获取结果时切换线程 deferredResult.await()?.let { data -> withContext(Dispatchers.Main) { showData(data) } } ``` **步骤 3:绑定生命周期(防止内存泄漏)** 在 Android 中推荐使用 `lifecycleScope` 或 `viewModelScope`: ```kotlin // Activity/Fragment 中 lifecycleScope.launch(Dispatchers.IO) { // 自动跟随生命周期取消协程[^5] } ``` #### 3. **协程与 Handler 的对比** | 特性 | Handler | 协程 | |---------------------|-----------------------------|----------------------------------------------------------------------| | **线程切换** | 需手动调用 `post()` | 通过 `Dispatchers.Main/IO/Default` 自动切换[^5] | | **代码复杂度** | 需多层回调 | 使用同步写法实现异步逻辑 | | **生命周期管理** | 易内存泄漏需手动释放 | 通过 `CoroutineScope` 自动取消[^5] | | **返回值处理** | 无法直接获取 | 可通过 `async`+`await` 获取返回值[^3] | #### 4. **实际案例** **网络请求 + 更新 UI** ```kotlin fun loadData() { viewModelScope.launch { try { // 后台线程执行请求 val response = withContext(Dispatchers.IO) { apiService.fetchData() } // 主线程更新 UI _uiState.value = Success(response) } catch (e: Exception) { _uiState.value = Error(e) } } } ``` #### 5. **注意项** - **异常处理**:使用 `try-catch` 包裹可能出错的代码块 - **取消协程**:通过 `job.cancel()` 手动取消,或依赖 `lifecycleScope` 自动管理 - **避免 GlobalScope**:优先使用绑定生命周期的 Scope(如 `viewModelScope`)[^4] --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值