19、生命周期与感知:Lifecycle与Compose集成、LifecycleEventEffect、配置变更处理

生命周期管理,是Android开发里绕不开的话题。我记得刚接触Android那会儿,Activity的生命周期回调让我吃了不少苦头——onCreate里初始化、onPause里暂停、onDestroy里释放,稍有不慎就是内存泄漏或者空指针。到了Compose时代,情况有了很大变化,但生命周期的概念依然存在,只是换了一种更优雅的方式和我们打交道。

说白了,Compose的生命周期感知,核心就是让我们的UI代码能自动响应Activity或Fragment的生命周期变化。你不用再手动写一堆onStart、onStop的回调了,Compose帮你封装好了。

19.1 Lifecycle与Compose的集成方式

在Compose中,我们通过LocalLifecycleOwner这个CompositionLocal来获取当前的生命周期所有者。默认情况下,Compose会从最近的Activity或Fragment中拿到LifecycleOwner。

我个人的习惯是,在ViewModel里尽量不直接持有Lifecycle的引用,而是通过Compose层去感知。这样ViewModel保持纯Kotlin,测试起来也方便。

来看一个最简单的例子:

@Composable
fun LifecycleAwareScreen() {
    val lifecycleOwner = LocalLifecycleOwner.current
    val lifecycle = lifecycleOwner.lifecycle

    // 观察生命周期状态变化
    DisposableEffect(lifecycleOwner) {
        val observer = LifecycleEventObserver { _, event ->
            when (event) {
                Lifecycle.Event.ON_START -> Log.d("Lifecycle", "Screen started")
                Lifecycle.Event.ON_STOP -> Log.d("Lifecycle", "Screen stopped")
                else -> {}
            }
        }
        lifecycle.addObserver(observer)
        onDispose {
            lifecycle.removeObserver(observer)
        }
    }

    Text("当前生命周期状态: ${lifecycle.currentState}")
}

嗯,这里要注意:DisposableEffect的key如果传了lifecycleOwner,那么当lifecycleOwner变化时,旧的observer会被清理,新的会被注册。这在Fragment切换场景下特别有用。

19.2 LifecycleEventEffect:更简洁的写法

每次都要写DisposableEffect加LifecycleEventObserver,说实话有点啰嗦。好在官方提供了LifecycleEventEffect这个扩展函数,让代码清爽很多。

我曾经在一个项目里,需要监听Activity的onResume来做数据刷新。如果用传统方式,得在Activity里写onResume回调,然后通知ViewModel。用Compose之后,一行代码搞定:

@Composable
fun DataRefreshScreen(viewModel: MyViewModel) {
    // 当生命周期到达ON_RESUME时执行
    LifecycleEventEffect(Lifecycle.Event.ON_RESUME) {
        viewModel.refreshData()
    }

    // 也可以监听多个事件
    LifecycleEventEffect(Lifecycle.Event.ON_PAUSE) {
        viewModel.saveDraft()
    }

    // UI内容
    val data by viewModel.data.collectAsState()
    Text("数据: $data")
}

核心要点:LifecycleEventEffect会在Composable进入组合时开始监听,离开组合时自动移除监听。你不需要手动管理注册和注销。

你想想看,这比在Activity里写onResume、onPause要省多少事?而且代码就在Composable函数里,逻辑内聚性更强。

19.3 配置变更处理:不再需要onSaveInstanceState

配置变更,比如屏幕旋转、语言切换、字体大小变化,在传统View体系里是个麻烦事。Activity会被销毁重建,你得用onSaveInstanceState保存状态,然后在onCreate里恢复。

在Compose里,情况完全不同。因为Compose的状态管理机制天然支持配置变更。只要你的状态是用remembermutableStateOf管理的,并且ViewModel是通过viewModel()获取的,配置变更后状态会自动保留。

为什么会这样?因为ViewModel的生命周期比Activity长,它不会因为配置变更而销毁。而Compose的remember在重组时会保留之前的值。

来看一个实际例子:

@Composable
fun RotationSafeScreen() {
    // 这个状态在屏幕旋转后依然保留
    var count by remember { mutableStateOf(0) }

    // ViewModel在配置变更后也不会丢失
    val viewModel: MyViewModel = viewModel()

    Column(modifier = Modifier.padding(16.dp)) {
        Text("点击次数: $count")
        Button(onClick = { count++ }) {
            Text("点击")
        }

        Text("ViewModel中的数据: ${viewModel.someData}")
    }
}

我的建议:如果你有需要在配置变更后保留的UI状态,优先用rememberSaveable而不是remember。因为rememberSaveable会利用Bundle机制,即使进程被杀死也能恢复。我在一个表单页面里吃过亏,用户填了一半旋转屏幕,数据全丢了,后来换成rememberSaveable才解决。

19.4 生命周期感知的完整流程图

下面这张图展示了Compose中生命周期感知的核心流程,从Composable组合到生命周期事件响应,再到状态恢复:

Compose生命周期感知核心流程 Composable 组合 LocalLifecycleOwner.current 获取当前生命周期所有者 方式一:DisposableEffect 手动添加/移除Observer 灵活但代码稍多 方式二:LifecycleEventEffect 一行代码监听事件 简洁、推荐使用 响应生命周期事件 ON_START / ON_RESUME / ON_PAUSE 配置变更自动恢复 remember / rememberSaveable / ViewModel

19.5 实战中的避坑指南

我在项目中遇到过几个典型的生命周期问题,分享出来给大家参考:

我曾经踩过的坑:

  • 在LaunchedEffect里直接使用lifecycle:LaunchedEffect的生命周期和Composable绑定,但如果你在里面直接访问LocalLifecycleOwner.current,可能会拿到过时的引用。建议在LaunchedEffect外部获取lifecycle,然后作为参数传进去。
  • 忘记处理ON_DESTROY:有些资源(比如传感器监听、蓝牙连接)必须在ON_DESTROY时释放。虽然Compose的DisposableEffect会在离开组合时自动清理,但如果你用了LifecycleEventEffect,记得也要监听ON_DESTROY事件。
  • 配置变更后ViewModel里的LiveData重复观察:这个问题比较隐蔽。如果ViewModel里用LiveData,而Composable里用observeAsState,配置变更后ViewModel不会重建,但Composable会重组。如果LiveData的观察者注册逻辑写在了重组路径里,可能会导致重复注册。解决方案是用collectAsState配合Flow,或者确保观察者注册在remember块里。

19.6 最佳实践总结

场景 推荐做法 说明
监听生命周期事件 LifecycleEventEffect 代码简洁,自动管理注册注销
需要更细粒度控制 DisposableEffect + LifecycleEventObserver 适合需要自定义observer的场景
配置变更保留UI状态 rememberSaveable 比remember更强,进程杀死也能恢复
配置变更保留业务数据 ViewModel + StateFlow ViewModel生命周期长,Flow天然支持
需要感知生命周期做动画 LifecycleResumeEffect 专门用于动画场景的扩展

个人经验:我一般会在项目里建一个LifecycleUtils.kt文件,把常用的生命周期监听封装成扩展函数。比如fun LifecycleOwner.onResume(action: () -> Unit),这样团队里其他人用起来也方便。代码复用率很高,推荐你也试试。

好了,关于生命周期与Compose的集成,核心就是这些。记住一点:Compose帮你管理了大部分生命周期细节,你只需要关注业务逻辑。但理解背后的原理,能让你在遇到奇怪问题时快速定位。嗯,就这样。


公众号:蓝海资料掘金营,微信deep3321