【Android开发小技巧】扔掉这坑人的 Handler

2,459 阅读1分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动

1. 坑人的 Handler

大家都知道 Handler 特别坑,使用不当会造成各种问题:

  • Activity 中使用 Handler 有可能会造成 Context 内存泄漏;
  • Handler() 默认构造函数会因为缺少 Looper 而崩溃(虽然已标位 deprecated ) ;
  • View.post/postDelayed 基于 Handler 实现,在 View 已经 detached 时可能仍在执行,造成异常

诸如上述这些问题让开发者们防不胜防,但是 Handler 便利性又让开发者们难以割舍。大家希望寻找一种同样方便但更安全的替代方案。如今借助 Kotlin Coroutine + Lifecycle 我们可以做到这一切,思路很简单:利用协程执行异步任务,同时绑定 Lifecycle ,在必要的时候终止任务

2. 替代 Handler.post/postDelayed

项目中添加 coroutie 和 lifecycle 依赖:

implementation "andoridx.lifecycle:lifecycle-runtime-ktx:2.3.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3"

代码如下

fun LifecycleOwner.postDelayedOnLifecycle(
    duration: Long,
    dispatcher: CoroutineDispatcher = Dispatchers.Main,
    block: () -> Unit
): Job = lifecycleScope.launch(dispatcher) {
    delay(duration)
    block()

}

因为 Handler.post 运行在 UI 线程, 所以 Dispatcher 默认使用 Dispatchers.MainpostDelayed 的延时使用 delay 实现。

使用效果如下,在 Activity 或 Fragment 中无需再依赖 Handler 了

class MainActivity : AppCompatActivity() {
  
    ...
    postDelayedOnLifecycle(500L) {
      //Do something
    }
    ...

}

3. 替代 View.post/postDelayed

我们还可以借助 lifecycle-ktx 提供的 View 的扩展方法 findViewTreeLifecycleOwner(),替代 View.post / View.postDelayed , findViewTreeLifecycleOwner 可以从当前 View 最近的 Fragment 或者 Activity 获取 LifecycleOwner。

代码如下:

fun View.postDelayedOnLifecycle(
    duration: Long,
    dispatcher: CoroutineDispatcher = Dispatchers.Main,
    block:() -> Unit
) : Job? = findViewTreeLifecycleOwner()?.let { lifecycleOwner ->
    lifecycleOwner.lifecycleScope.launch(dispatcher) {
        delay(duration)
        block()
    }
}

所以, Handler 是不是可以彻底 Deprecated 了 ?