Compose 页面生命周期完全指南 Compose 的生命周期不是单一概念,而是三层叠加模型 :
Android 生命周期 (LifecycleOwner)—— Activity / Fragment 的传统生命周期组合生命周期 (Composition)—— Composable 进入/离开组合树Effect 生命周期 (LaunchedEffect / DisposableEffect)—— 副作用随 key 变化理解这三层如何协作,是写出无内存泄漏、无状态错乱的 Compose 代码的关键。
一、三层生命周期全景图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 onCreate () → Composition → Composition onStart () 首次进入 重组 onResume () │ │ │ ▼ ▼ ▼ ┌─────────────────────────────────────────────────┐ │ Activity Lifecycle │ │ Lifecycle.Event .ON_START / ON_RESUME / ... │ └─────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────┐ │ Composition(组合) │ │ 进入组合 → 重组(recomposition) → 退出组合 │ └─────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────┐ │ Effect(副作用) │ │ LaunchedEffect → key 变化 → 协程取消 │ │ DisposableEffect → key 变化 → dispose → 重启 │ └─────────────────────────────────────────────────┘
关键区别 :
层次 粒度 触发时机 主要用途 Android Lifecycle 页面级 onStart / onResume / onStop 整体可见性、前后台切换 Composition 组件级 首次组合 / 重组 / 退出 UI 渲染、remember Effect 副作用 key 变化 / 退出组合 协程、监听器、资源管理
二、Composition 生命周期 —— 进入与退出 2.1 进入组合(Enter Composition) Composable 函数首次被调用并加入组合树 :
1 2 3 4 5 6 7 8 9 10 11 12 13 @Composable fun MyScreen () { val name = remember { "World" } DisposableEffect(Unit ) { println("📌 Entered composition" ) onDispose { println("📌 Left composition" ) } } Text("Hello $name " ) }
2.2 重组(Recomposition) 当 State 发生变化,依赖该 State 的 Composable 会被重新执行 :
1 2 3 4 5 6 7 8 9 10 @Composable fun Counter () { var count by remember { mutableIntStateOf(0 ) } Text("Count: $count " ) Button(onClick = { count++ }) { Text("Increment" ) } }
2.3 退出组合(Leave Composition) Composable 从组合树中移除(条件渲染、导航离开等):
1 2 3 4 5 6 @Composable fun Parent (showChild: Boolean ) { if (showChild) { Child() } }
三、Effect API —— 副作用四大件 Compose 提供 4 个 Effect API 来管理副作用。它们的核心区别在于何时执行 和何时清理 。
3.1 LaunchedEffect —— 协程的入口 key 变化 → 取消旧协程 → 启动新协程;退出组合 → 取消协程 。
1 2 3 4 5 6 7 8 9 10 11 @Composable fun LoadUserData (userId: String ) { var user by remember { mutableStateOf<User?>(null ) } LaunchedEffect(userId) { user = api.fetchUser(userId) } user?.let { Text(it.name) } ?: CircularProgressIndicator() }
LaunchedEffect 生命周期时序 :
1 2 3 4 5 6 7 8 userId="a" userId="b" 退出组合 │ │ │ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ │协程: a │ → │取消 a │ → │取消 b │ │fetch... │ │协程: b │ │ │ │ │ │fetch... │ │ │ └─────────┘ └─────────┘ └─────────┘
3.2 DisposableEffect —— 需要清理的副作用 **key 变化 → dispose() → 重新 enter();退出组合 → dispose()**。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Composable fun ObserveUserStatus (userId: String ) { DisposableEffect(userId) { val observer = object : Observer { override fun onChange (status: String ) { } } api.registerObserver(userId, observer) onDispose { api.unregisterObserver(userId, observer) } } }
DisposableEffect 典型场景 :
场景 注册 onDispose Lifecycle 观察 lifecycle.addObserver(...)lifecycle.removeObserver(...)广播接收器 context.registerReceiver(...)context.unregisterReceiver(...)传感器监听 sensorManager.registerListener(...)sensorManager.unregisterListener(...)EventBus EventBus.register(...)EventBus.unregister(...)WebSocket ws.connect()ws.close()
3.3 SideEffect —— 每次重组后执行(无 key) 每次成功重组后调用,无清理逻辑 。适用于与非 Compose 状态同步:
1 2 3 4 5 6 7 8 9 @Composable fun AnalyticsScreen (screenName: String ) { val analytics = LocalAnalytics.current SideEffect { analytics.logScreenView(screenName) } }
3.4 rememberUpdatedState —— 保持引用最新 解决闭包捕获旧值的问题 。长生命周期的 Effect 需要获取最新状态,但又不希望 key 变化导致重启:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Composable fun LandingPage (onTimeout: () -> Unit ) { LaunchedEffect(Unit ) { delay(3000L ) onTimeout() } val currentOnTimeout by rememberUpdatedState(onTimeout) LaunchedEffect(Unit ) { delay(3000L ) currentOnTimeout() } }
3.5 Effect 对比总结 API key 变化 退出组合 每次重组后 有清理 可挂起 LaunchedEffect取消 + 重启 取消 否 自动取消协程 是 DisposableEffectdispose + 重启 dispose 否 手动 onDispose 否 SideEffect— — 是 否 否 rememberUpdatedState— — — — —
四、Android 生命周期集成 4.1 获取 LifecycleOwner 1 2 3 val lifecycleOwner = LocalLifecycleOwner.currentval lifecycle = lifecycleOwner.lifecycle
LocalLifecycleOwner 由 Navigation 或包含 ComposeView 的 Activity/Fragment 自动提供。
4.2 监听生命周期事件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Composable fun LifecycleAwareComponent () { val lifecycleOwner = LocalLifecycleOwner.current DisposableEffect(lifecycleOwner) { val observer = LifecycleEventObserver { _, event -> when (event) { Lifecycle.Event.ON_START -> println("👀 页面可见" ) Lifecycle.Event.ON_STOP -> println("🙈 页面不可见" ) Lifecycle.Event.ON_RESUME -> println("👉 获得焦点" ) Lifecycle.Event.ON_PAUSE -> println("👈 失去焦点" ) else -> {} } } lifecycleOwner.lifecycle.addObserver(observer) onDispose { lifecycleOwner.lifecycle.removeObserver(observer) } } }
4.3 常用生命周期事件含义 1 2 3 4 5 6 7 8 9 10 11 ON_CREATE ═══ ON_START ═══ ON_RESUME │ │ │ │ │ [页面在前台,可交互] │ [页面可见] │ [初始化完成] │ ON_PAUSE │ │ ▼ ▼ ON_STOP ← [部分不可见] │ ▼ ON_DESTROY
4.4 组合 Lifecycle 与 StateFlow 的协程 **核心 API:repeatOnLifecycle**。在正确的生命周期状态下收集 flow,自动暂停/恢复:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Composable fun ObserveWithLifecycle (viewModel: MyViewModel ) { val lifecycleOwner = LocalLifecycleOwner.current val uiState by produceState<UiState>( initialValue = UiState.Loading, key1 = lifecycleOwner, key2 = viewModel, ) { lifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.uiState.collect { value = it } } } }
更简洁的方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class MyViewModel : ViewModel () { private val _uiState = MutableStateFlow(UiState.Loading) val uiState: StateFlow<UiState> = _uiState.asStateFlow() } @Composable fun MyScreen (viewModel: MyViewModel = viewModel() ) { val lifecycleOwner = LocalLifecycleOwner.current val uiState by viewModel.uiState .flowWithLifecycle( lifecycle = lifecycleOwner.lifecycle, minActiveState = Lifecycle.State.STARTED ) .collectAsStateWithLifecycle(initialValue = UiState.Loading) }
collectAsStateWithLifecycle(推荐) :
1 2 3 4 5 6 7 8 @Composable fun MyScreen (viewModel: MyViewModel = viewModel() ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() }
4.5 生命周期状态速查 状态 何时进入 适合做的事情 CREATEDActivity 创建后 一次性初始化 STARTED页面可见 开始收集 Flow、动画、网络请求 RESUMED页面在前台可交互 相机、位置更新、高优先级任务 PAUSED部分可见(如弹窗遮挡) 暂停相机、降低刷新频率 STOPPED完全不可见 停止 Flow 收集、释放重资源 DESTROYED销毁前 最终清理
五、remember 与 rememberSaveable 5.1 remember —— 跨重组保留 1 2 3 4 5 6 7 8 9 @Composable fun RememberExample () { var count by remember { mutableIntStateOf(0 ) } var badCount by mutableIntStateOf(0 ) }
5.2 rememberSaveable —— 跨进程死亡保留 1 2 3 4 5 6 7 8 9 10 11 @Composable fun SearchScreen () { var query by rememberSaveable { mutableStateOf("" ) } OutlinedTextField( value = query, onValueChange = { query = it }, label = { Text("Search" ) } ) }
方式 跨重组 跨配置变更 跨进程死亡 remember是 否 否 rememberSaveable是 是 是 ViewModel是 是 否(需 SavedStateHandle) ViewModel + SavedStateHandle是 是 是
5.3 自定义 Saveable Saver 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 data class User (val name: String, val age: Int )val UserSaver = run { val nameKey = "name" val ageKey = "age" mapSaver( save = { mapOf(nameKey to it.name, ageKey to it.age.toString()) }, restore = { User(it[nameKey]!!, it[ageKey]!!.toInt()) } ) } @Composable fun UserScreen () { var user by rememberSaveable(stateSaver = UserSaver) { mutableStateOf(User("Alice" , 25 )) } }
六、ViewModel 生命周期 6.1 ViewModel 何时销毁 1 2 3 4 5 6 7 8 9 10 Activity/Fragment 创建 │ ▼ ViewModel 创建 │ │ ... 配置变更(旋转屏幕)... ViewModel 不销毁! │ ▼ onCleared( ) ( Activity 真正销毁 / Fragment 移除)
6.2 ViewModel + SavedStateHandle 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class EditorViewModel ( private val savedStateHandle: SavedStateHandle ) : ViewModel() { var title: String get () = savedStateHandle.get <String>("title" ) ?: "" set (value) { savedStateHandle["title" ] = value } var content: String get () = savedStateHandle.get <String>("content" ) ?: "" set (value) { savedStateHandle["content" ] = value } } @Composable fun EditorScreen (viewModel: EditorViewModel = viewModel() ) { OutlinedTextField( value = viewModel.title, onValueChange = { viewModel.title = it }, ) }
6.3 ViewModel 初始化时机(懒加载) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 sealed interface EditorUiState { data object Loading : EditorUiState data class Ready (val title: String, val content: String) : EditorUiState } class EditorViewModel ( private val savedStateHandle: SavedStateHandle ) : ViewModel() { private val _uiState = MutableStateFlow<EditorUiState>(EditorUiState.Loading) val uiState: StateFlow<EditorUiState> = _uiState.asStateFlow() init { loadContent() } private fun loadContent () { val title = savedStateHandle.get <String>("title" ) ?: "" val content = savedStateHandle.get <String>("content" ) ?: "" _uiState.value = EditorUiState.Ready(title, content) } }
七、CompositionLocal —— 作用域内生命周期 CompositionLocal 的值在组合树的某个节点提供,子节点退出组合时自动不可见:
1 2 3 4 5 6 7 8 9 val LocalThemeColor = compositionLocalOf { Color.Unspecified }@Composable fun ThemeProvider (color: Color , content: @Composable () -> Unit ) { CompositionLocalProvider(LocalThemeColor provides color) { content() } }
八、配置变更 —— 屏幕旋转、语言切换等 资源 配置变更时行为 进程死亡时行为 remember丢失 丢失 rememberSaveable保留 保留 ViewModel保留 丢失 ViewModel + SavedStateHandle保留 保留 object / companion 单例保留 丢失 DataStore / SharedPreferences 保留 保留
最佳实践 :
1 2 3 4 5 6 7 8 9 10 11 12 13 @Composable fun OrderScreen (orderId: String ) { var expanded by rememberSaveable { mutableStateOf(false ) } val viewModel: OrderViewModel = viewModel() val order by viewModel.order.collectAsStateWithLifecycle() LaunchedEffect(orderId) { viewModel.loadOrder(orderId) } }
九、页面生命周期实战场景 9.1 回到前台时刷新数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Composable fun RefreshOnResume (viewModel: DataViewModel ) { val lifecycle = LocalLifecycleOwner.current.lifecycle DisposableEffect(lifecycle) { val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME) { viewModel.refresh() } } lifecycle.addObserver(observer) onDispose { lifecycle.removeObserver(observer) } } }
9.2 离开页面时暂停视频 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Composable fun VideoPlayer (videoId: String ) { val lifecycle = LocalLifecycleOwner.current.lifecycle DisposableEffect(lifecycle) { val observer = LifecycleEventObserver { _, event -> when (event) { Lifecycle.Event.ON_PAUSE -> player.pause() Lifecycle.Event.ON_RESUME -> player.resume() Lifecycle.Event.ON_STOP -> player.release() else -> {} } } lifecycle.addObserver(observer) onDispose { lifecycle.removeObserver(observer) player.release() } } }
9.3 秒杀倒计时(退出页面自动取消) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Composable fun FlashSaleCountdown (endTime: Long ) { var remaining by remember { mutableLongStateOf(endTime - System.currentTimeMillis()) } LaunchedEffect(Unit ) { while (remaining > 0 ) { delay(1000 ) remaining = (endTime - System.currentTimeMillis()).coerceAtLeast(0 ) } } Text("${remaining / 1000 } 秒后结束" ) }
9.4 权限请求(关联生命周期) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Composable fun RequestCameraPermission (onGranted: () -> Unit ) { val lifecycleOwner = LocalLifecycleOwner.current val launcher = rememberLauncherForActivityResult( contract = ActivityResultContracts.RequestPermission() ) { isGranted -> if (isGranted) onGranted() } DisposableEffect(lifecycleOwner) { val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME) { launcher.launch(android.Manifest.permission.CAMERA) } } lifecycleOwner.lifecycle.addObserver(observer) onDispose { lifecycleOwner.lifecycle.removeObserver(observer) } } }
9.5 定时器 + 可见性联动 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Composable fun AutoRefreshFeed (viewModel: FeedViewModel ) { val lifecycle = LocalLifecycleOwner.current.lifecycle LaunchedEffect(lifecycle) { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { while (true ) { delay(30_000 ) viewModel.refresh() } } } }
十、常见生命周期错误与修复 问题 原因 修复 协程泄漏 LaunchedEffect key 用 Unit 但外部依赖变化key 传入依赖项(如 userId) Flow 在后台仍然收集 直接用 collectAsState() 改用 collectAsStateWithLifecycle() 或 repeatOnLifecycle 配置变更后状态丢失 用了 remember 存重要状态 改用 rememberSaveable 或 SavedStateHandle ViewModel 重新创建 在 Composable 函数内手动 ViewModel() 且传了错误参数 确保 key 参数正确;优先使用 viewModel() 工厂 动画在 ON_STOP 时继续 动画未绑定生命周期 用 LaunchedEffect + repeatOnLifecycle 包裹动画循环 onDispose 不执行DisposableEffect key 用了可变对象key 使用 stable 类型(String、Int、Unit 等) 闭包捕获过期值 Effect 启动后 lambda 被替换 用 rememberUpdatedState 包装回调
十一、生命周期相关依赖 1 2 3 4 5 6 7 8 9 10 11 12 13 dependencies { implementation("androidx.compose.runtime:runtime" ) implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7" ) implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.7" ) implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.7" ) }
十二、生命周期决策速查表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 需要做一件事,放在哪里? ┌─ 是否需要在「每次重组」后执行? │ ├─ 是 → SideEffect { } │ └─ 否 ↓ │ ├─ 是否需要在「离开组合 / key 变化」时清理? │ ├─ 是 → DisposableEffect(key) { onDispose { } } │ └─ 否 ↓ │ ├─ 是否需要「协程 / delay / 异步」? │ ├─ 是 → LaunchedEffect(key) { } │ └─ 否 ↓ │ ├─ 是否需要「依赖 Android 生命周期」? │ ├─ 是 → DisposableEffect(lifecycle) + repeatOnLifecycle │ └─ 否 ↓ │ ├─ 是否「UI 状态」需要在配置变更后恢复? │ ├─ 是 → rememberSaveable { } │ └─ 否 → remember { } │ └─ 是否「业务数据」需要在进程死亡后恢复? ├─ 是 → ViewModel + SavedStateHandle └─ 否 → ViewModel
全文覆盖 Compose 生命周期三层模型(Android Lifecycle / Composition / Effect)、四大 Effect API、rememberSaveable、repeatOnLifecycle、5 个实战场景和 7 类常见错误修复方案。