23、生命周期:LifecycleOwner、LifecycleEventEffect、DisposableEffect
生命周期,这个词在 Android 开发里太常见了。以前写 XML 布局时,我们得在 Activity 或 Fragment 里手动处理 onStart、onStop、onDestroy 这些回调。稍不留神,就会搞出内存泄漏或者空指针。
到了 Compose 时代,事情变得不一样了。Compose 是声明式的,它不直接跟 Activity 或 Fragment 的生命周期挂钩。但我们的代码总得知道「什么时候该干活,什么时候该收手」对吧?
所以就有了今天要聊的三个东西:LifecycleOwner、LifecycleEventEffect、DisposableEffect。它们就是 Compose 世界里管理生命周期的三把钥匙。
LifecycleOwner:谁在管生命周期?
说白了,LifecycleOwner 就是一个接口。谁实现了它,谁就能提供生命周期状态。Activity 和 Fragment 都实现了这个接口。在 Compose 里,我们经常需要拿到这个对象,来监听生命周期的变化。
怎么拿?很简单:
val lifecycleOwner = LocalLifecycleOwner.current
嗯,这里要注意。LocalLifecycleOwner 是一个 CompositionLocal,它会在 Compose 树里自动传递。你不需要手动传参,直接取就行。
我个人习惯在 ViewModel 里尽量不直接依赖 LifecycleOwner,而是用 LiveData 或者 Flow 来间接感知。但有些场景,比如需要监听 Activity 的 onResume 来做一些统计上报,那就绕不开它了。
LifecycleEventEffect:监听事件的好帮手
LifecycleEventEffect 是 Compose 1.5 之后引入的一个实验性 API。它的作用很简单:当生命周期事件发生时,执行一段代码。
用法长这样:
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
fun MyScreen() {
LifecycleEventEffect(Lifecycle.Event.ON_RESUME) {
// 每次 resume 时执行
logScreenView()
}
}
我在项目中遇到过一个问题:用 LaunchedEffect 配合 lifecycle.repeatOnLifecycle 来做数据加载,结果发现每次配置变更都会重新执行。后来换成 LifecycleEventEffect,只监听 ON_RESUME,就解决了。
不过要提醒你,这个 API 目前还是实验性的。生产环境用的话,记得加 @OptIn 注解。我个人建议,如果项目比较保守,可以继续用下面的 DisposableEffect。
DisposableEffect:最通用的生命周期管理
DisposableEffect 是 Compose 官方推荐的生命周期管理方式。它跟 LaunchedEffect 很像,但多了一个「清理」的步骤。
看个例子:
@Composable
fun LocationTracker(
onLocationChanged: (Location) -> Unit
) {
val context = LocalContext.current
DisposableEffect(context) {
val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
val listener = LocationListener { location ->
onLocationChanged(location)
}
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
1000L,
0f,
listener
)
// 清理工作
onDispose {
locationManager.removeUpdates(listener)
}
}
}
为什么说它好用?因为 onDispose 里的代码,会在 Composable 离开组合时自动执行。你想想看,以前写监听器,最怕的就是忘记注销。现在好了,DisposableEffect 强制你写清理逻辑,想忘都难。
我曾经踩过一个坑:在 DisposableEffect 的 key 里传了一个会变的对象,结果每次重组都重新执行,导致监听器反复注册注销。后来改成用稳定的 key,比如 context 或者 lifecycle,就稳了。
三者对比:什么时候用哪个?
| API | 适用场景 | 特点 |
|---|---|---|
| LifecycleOwner | 需要获取当前生命周期状态 | 通过 CompositionLocal 获取,配合 lifecycle.currentState 使用 |
| LifecycleEventEffect | 监听特定生命周期事件 | 实验性 API,写法简洁,适合统计上报等场景 |
| DisposableEffect | 需要注册/注销资源 | 最通用,强制清理,适合监听器、传感器、动画等 |
避坑指南
我曾经在 DisposableEffect 里直接调用了 ViewModel 的方法,结果发现 ViewModel 在 onDispose 时已经被清空了。后来才意识到,DisposableEffect 运行在 Composition 的协程作用域里,而 ViewModel 的生命周期比 Composable 长。正确的做法是:把 ViewModel 的清理逻辑放在 ViewModel 自己的 onCleared 里,而不是 DisposableEffect 里。
还有一个坑:LifecycleEventEffect 的 key 参数。如果你不传 key,它默认用 Unit,也就是说只会在第一次进入时注册。如果你希望每次重组都重新注册,就得传一个会变的 key。但这样做容易导致性能问题,慎用。
知识体系图
下面这张图,帮你理清这三个 API 的关系和适用边界:
总结
生命周期管理,说白了就是「什么时候开始,什么时候结束」。Compose 给了我们三种方式,各有各的适用场景。
我个人最常用的是 DisposableEffect,因为它最灵活,也最安全。LifecycleEventEffect 虽然简洁,但毕竟是实验性的,我一般只在个人项目里用。LifecycleOwner 更多是作为基础设施,配合其他 API 一起使用。
嗯,最后说一句:不管用哪种方式,记得把清理逻辑写完整。内存泄漏这东西,一旦发生,排查起来真的很痛苦。
核心要点:
- DisposableEffect 的 onDispose 是强制执行的,别偷懒不写
- LifecycleEventEffect 的 key 要谨慎选择,避免不必要的重复执行
- LifecycleOwner 通过 LocalLifecycleOwner.current 获取,不要手动传参
小技巧:如果你需要在 ViewModel 里感知生命周期,可以用 lifecycle.repeatOnLifecycle 配合 Flow,这样既干净又安全。
警告:不要在 DisposableEffect 里直接调用 ViewModel 的清理方法。ViewModel 的生命周期比 Composable 长,清理逻辑应该放在 ViewModel 的 onCleared 里。