公众号「稀有猿诉」 原文链接 降Compose十八掌之『羝羊触蕃』| Handle Platform Lifecycles
Jetpack Compose是一个独立的声明式UI框架,它并不受限于任何操作系统平台,从框架定位的角度来讲,它是跨平台的,也应该要跨平台。但是我们的应用程序必然是为某些操作系统平台(后面简称平台Platform)构建的,也就是说要运行在某些平台上面。这就免不了要与平台进行打交道。这篇文章将以Android平台为例,学习在Compose中如何处理平台的生命周期事件。
感知平台生命周期事件
对于移动应用程序来说,感知平台的生命周期是非常重要的,比如最为典型的场景,对于一个新闻消息类的应用来说,当首次进入页面的时候肯定 要刷新拉取最新的消息,当用户切换到另外一个应用时,比如接了个电话,或者分享,之后再回到你的应用页面,这时也应该主动刷新消息,而不是要等着用户手动的去点击刷新按扭;再比如说当使用了硬件资源(位置,Camera或者Sensors等)时,更是要当离开应用页面的时候就应该立即释放硬件,以停止对硬件资源的占用。
从前面的文章降Compose十八掌之『损则有孚』| Lifecycle中我们了解到Composable本身的生命周期与平台是无关的且非常不一致,光靠Compose自己的节奏是无法感知到在平台生命周期事件的。这就需要我们使用一些桥接工具来感知平台生命周期事件,以能让我们针对感兴趣的事件执行一些操作。
生命周期事件副作用函数(LifecycleEffects)
幸运的是Jetpack组件中的lifecycle已经添加了对Compose的支持,定义了一些生命周期副作用函数,在这些副作用函数中可以针对 不同的事件设置代码块,当相应的生命周期发生时就会执行这些代码块:
LifecycleEventEffect(Lifecycle.Event.ON_START) {
// onStar时执行一些操作
}
上面的代码就是指定要在onStart时做一些事情。需要注意的是,无法监听onDestroy(即事件Lifecycle.Event.ON_DESTROY),因为Compose的组合会在onDestroy之前就结束了。
除了上面的用法之外,还有更为为方便的LifecycleStartEffect和LifecycleResumeEffect可以直接使用,它们是针对onStart/onStop和onResume/onPause两对事件的,因为生命周期中最为常用的就是这四个事件了:
LifecycleStartEffect {
// onStart中需要做的事情
onStopOrDispose {
// onStop需要做的事情
}
}
LifecycleResumeEffect {
// onResume需要做的事情
onPauseOrDispose {
// onPause需要做的事
}
}
需要注意,这两个副作用函数是针对事件对的,也就是说必须要带着后面的onStopOrDispose和onPauseOrDispose。如果仅对onStart感兴趣,而无须在onStop中做清理,那么应该直接使用LifecycleEventEffect(Lifecycle.Event.ON_START) {}(对于onResume也是同理)。
扩展阅读:
监听生命周期事件
除了直接使用生命周期副作用函数以外,也可以用lifecycle原生的方式,直接向LifecycleOwner注册一个LifecycleEventObserver来监听生命周期。通过Compose提供的LifeCycleOwner.current可以获得当前的LifecycleOwner,然后向其注册一个LifecycleEventObserver,当平台生命周期发生变化时,就会带着事件类型回调给监听者,监听者可以针对感兴趣的事件做操作。还需要注意的是,需要在组合结束(离开)时反注册observer,因此这里要用DisposableEffect。对于副作用函数不熟悉的同学可以去复习一下降Compose十八掌之『龙战于野』| Side Effects。
来看个简单的示例:
val lifecycleOwner = LocalLifecycleOwner.current
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_CREATE -> { /* onCreate */ }
Lifecycle.Event.ON_START -> { /* onStart */ }
Lifecycle.Event.ON_RESUME -> { /* onResume */ }
Lifecycle.Event.ON_PAUSE -> { /* onPause */ }
Lifecycle.Event.ON_STOP -> { /* onStop */ }
Lifecycle.Event.ON_DESTROY -> { /* onDestroy */ }
Lifecycle.Event.ON_ANY -> { /* Any event */ }
else -> {}
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
这样就可以监听到生命周期事件,然后针对不同的事件做相应的操作。
当然,如果事件不止做一件事情,或者说对事件的响应不光光是执行一些函数,可能还会有页面的修改,那么这时最好就是把事件保存为一个状态(State),更为方便:
val lifecycleOwner = LocalLifecycleOwner.current
var lifecycleEvent by remember { mutableStateOf(Lifecycle.Event.ON_ANY) }
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
lifecycleEvent = event
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
LaunchedEffect(lifecycleEvent) {
if (lifecycleEvent == Lifecycle.Event.ON_RESUME) {
viewModel.refresh()
}
}
Column() {
if (lifecycleEvent == Lifecycle.Event.ON_RESUME) {
Text("Welcome back")
}
}
扩展阅读:
- Android handle lifecycle event on Jetpack Compose Screen
- Jetpack Compose — Making Composable lifecycle-aware
- Jetpack Compose with Lifecycle-Aware Composables
以数据流的方式来处理生命周期事件
生命周期是由系统控制,不时发生变化,每次变化会向监听者回调一个事件,如果以一定的时间跨度来看待,这些事件就形成了一个数据流。因此,Lifecycle还提供了一个Flow接口,用以发送Lifecycle事件。可以当作状态(State)来收集此Flow,这样事件的变化就能驱动Compose的重组,进而感知到最新的生命周期事件:
val lifecycleOwner = LocalLifecycleOwner.current
val stateFlow = lifecycleOwner.lifecycle.currentStateFlow
val currentLifecycleState by stateFlow.collectAsState()
// 或者
val lifecycleOwner = LocalLifecycleOwner.current
val currentLifecycleState = lifecycleOwner.lifecycle.currentStateAsState()
注意: 对于Flow不熟悉的同学可以复习一下包教包会的Kotlin Flow教程。
不要在ViewModel中感知生命周期
根据现代安卓开发架构原则,ViewModel应该处理与UI相关的业务逻辑,它应该独立于平台,因此,千万不要在ViewModel去感知生命周期,事实上你也做不到,因为ViewModel是没任何对平台的依赖的,非常独立的一个类型,也即拿不到LifecycleOwner。
当然了,有同学说,我可以从Compose的Composable中把LifecycleOwner当作参数传给ViewModel,但仍然强烈不建议这样做。深层的原因在于,ViewModel是独立于平台的,它有自己的生命周周期,平台的组件(如Activity)是由系统控制的,但ViewModel是由我们自己控制的,它的生命周期要长于平台的组件,也就是说ViewModel的生命周期要长于它持有的LifecycleOwner,故LifecycleOwner可能会变得过时(非当前的Activity了),同时因为被更长生命的ViewModel持有,原LifecycleOwner可能无法被回收而引发内存泄漏。
ViewModel只应该负责业务逻辑相关的事情,在Composable中监听生命周期事件很方便,也很合适,然后调用ViewModel的相应的接口(如refresh())即可。
总结
得益于Jetpack中的Lifecycle组件,在Compose中感知生命周期没有想像中的那样难。在实际项目中,推荐使用更符合Compose的方式,也即生命周期副作用函数以及事件数据流。如果仅是在某些生命周期事件发生时执行一些操作,那就用LifecycleEventEffect函数;如果不止一处需要使用事件,那就用事件数据流。
欢迎搜索并关注 公众号「稀有猿诉」 获取更多的优质文章!
保护原创,请勿转载!