Android Compose 使用 RememberObserver/RetainObserver感知--对象在 remember/retain 中的生命周期
从了解到,会在组合进入时,启动一个协程…会在组合退出时,执行onDispose函数现在要讲的是: 通过rememberretain保存的状态对象,可分别搭配接口,实现感知 – 该对象在组合层次结构中,何时开始和停止被记住。
从 Android Compose 可组合项的生命周期、副作用API 了解到,LaunchedEffect 会在组合进入时,启动一个协程…DisposableEffect 会在组合退出时,执行 onDispose 函数
现在要讲的是: 通过 remember 、retain 保存的状态对象,可分别搭配 RememberObserver、RetainObserver 接口,实现感知 – 该对象在组合层次结构中,何时开始和停止被记住
remember+RememberObserver
实现示例1
若有个 媒体播放业务类:
class VideoPlayerManager {
fun initialize() {
Log.i("VideoPlayer", "VideoPlayer: 开始加载视频并准备播放...")
}
fun release() {
Log.i("VideoPlayer", "VideoPlayer: 停止播放,释放解码器和内存...")
}
fun play() {
Log.i("VideoPlayer", "VideoPlayer: 开始播放...")
}
}
在 composable 中,需要记住它的状态;并且,组合进入/退出时,能自动调用关联方法remember + RememberObserver :
@Composable
fun rememberVideoPlayerManager(): VideoPlayerManager {
// 不要公开暴露实现了 RememberObserver 的类
// 匿名内部类,隐藏 RememberObserver 的实现细节
return remember {
object : RememberObserver {
val manager = VideoPlayerManager()
// 成功进入 Compose 树时
override fun onRemembered() {
manager.initialize()
}
// 正常离开 Compose 树时
override fun onForgotten() {
manager.release()
}
// 重组被取消,根本没进入树时
override fun onAbandoned() {
// 如果 manager 的构造函数里分配了需要释放的资源,在这里释放。
// 如果只是简单的对象创建,通常不需要做什么。
Log.i("VideoPlayer", "onAbandoned")
}
}
}.manager // 只把普通的 Manager 返回给外界
}
在 Composable 界面中,点击按钮进行播放:
@Composable
fun VideoScreen() {
val playerManager = rememberVideoPlayerManager()
// UI 层只需要正常使用 playerManager 即可,不需要关心它的生命周期释放问题
Button(modifier = Modifier.padding(top = 100.dp), onClick = {
playerManager.play()
}) {
Text("Play")
}
}
当调用 `VideoScreen()`,进入组合时,输出 `VideoPlayer: 开始加载视频并准备播放`
点击 play 按钮,输出 `VideoPlayer: 开始播放`
退出该界面,输出 `VideoPlayer: 停止播放,释放解码器和内存`
实现示例2:使用协程
业务类中使用协程:
class MyDataController {
// 创建一个受控的 Job
private var job: Job? = null
// 使用主线程调度器创建作用域
private var coroutineScope: CoroutineScope? = null
fun onStart() {
job = Job()
coroutineScope = CoroutineScope(Dispatchers.Main + job!!)
// 在 onRemembered (对应这里的 onStart) 中启动协程
coroutineScope?.launch {
Log.i("controller", "onStart loadData")
loadData()
}
Log.i("controller", "onStart")
}
fun onStop() {
// 在 onForgotten 或 onAbandoned 中取消协程
job?.cancel()
job = null
coroutineScope = null
Log.i("controller", "onStop")
}
fun test() {
Log.i("controller", "test")
}
private suspend fun loadData() {
// 模拟网络请求
delay(2000)
println("数据加载完成")
}
}
在 composable 中,需要记住它的状态;并且,组合进入/退出时,能自动调用 onStart 、onStop 方法remember + RememberObserver :
@Composable
fun rememberDataController(): MyDataController {
return remember {
object: RememberObserver {
val controller = MyDataController()
override fun onRemembered() { // 进入
controller.onStart()
}
override fun onForgotten() { // 离开
controller.onStop()
}
override fun onAbandoned() { // 中断
Log.i("controller", "onAbandoned")
}
}/*.controller*/
}.controller
}
在 Composable 界面中的使用和之前是类似的(代码略)。
注意:若用成 `RememberObserver {}.controller;那最终的语义就是 remember { controller },即 就是 普通的 remember {},也就不会有感知等关联方法被调用了
retain+RetainObserver
retain 的生命周期会比 remember 更长,能在 配置更改时,保留状态。
复用上面的 MyDataController,提供 retain+RetainObserver 的 Composable 函数。
在 RetainObserver 内部维护一个更长生命的 CoroutineScope:
@Composable
fun retainDataController(): MyDataController {
return retain {
object: RetainObserver {
val controller = MyDataController()
private var scope: CoroutineScope? = null
override fun onEnteredComposition() { // 进入
Log.i("retain", "onEnteredComposition")
controller.onStart()
}
override fun onExitedComposition() { // 离开
Log.i("retain", "onExitedComposition")
controller.onStop()
}
override fun onRetained() { // 最先执行 保留:启动/建立资源
Log.i("retain", "onRetained: 创建长期资源")
// SupervisorJob() 子协程隔离:一个子协程异常取消后,不会影响其它子协程继续执行
// 普通的 Job, 同样情形下会中断属于该作用域的所有协程
scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
scope?.launch {
Log.i("retain", "开始预加载数据")
}
}
override fun onRetired() { // 退休了: 释放正常使用过的资源
Log.i("retain", "onRetired")
scope?.cancel()
}
override fun onUnused() { // 未使用: 对象创建出来了,但最终根本没进入“被保留”状态
Log.i("retain", "onUnused")
scope?.cancel()
}
}
}.controller
}
在 Composable 界面中的使用和之前是类似的(代码略)。
RetainObserver内部的CoroutineScope生命更长? 因为它在onRetained()被保留回调中执行Dispatchers.Main + SupervisorJob()表示该协程运行在主线程;且该协程内部的子协程,若是崩溃导致取消,不会影响其它子协程继续执行
RetainObserver 中的回调方法执行顺序:
- 初始执行
onRetainedonEnteredComposition
接着日志输出 controller的 onStart (因为这是在主线程执行)
再执行onRetained中的协程,输出 “开始预加载数据”
再执行controller的 onStart中的协程,输出 “onStart loadData”,此协程执行完成,输出 “数据加载完成”
- 配置更改后执行
onExitedComposition onRetired onRetained onEnteredComposition
在
onExitedComposition内 执行controller的 onStop
之后的日志输出同上面一样
- 退出界面执行
onExitedComposition onRetired
在
onExitedComposition内 执行controller的 onStop
更多推荐


所有评论(0)