副作用 (Side Effects) 全攻略:如何像大师一样掌控 Composable 的生命周期?

前言

在前面的文章中,我们反复强调:Composable 函数应该是纯净的(Pure)。 它的职责只是接收状态并渲染 UI。

但在真实的世界里,我们需要发起网络请求、需要弹一个 Toast、需要监听传感器的变化、或者需要初始化一个视频播放器。这些操作超出了"渲染 UI"的范畴,我们称之为 "副作用(Side Effects)"

作为资深开发,我们习惯于在 onCreateonResumeonDestroy 里处理这些逻辑。但在 Compose 这种"随心所欲"重组的环境下,如果处理不当,你的 App 可能会出现无限请求、内存泄漏甚至离奇崩溃。

今天,我们就来系统掌握 Compose 提供的副作用 API 家族


一、 为什么 Composable 函数体里不能直接写业务逻辑?

看这个典型的错误示范:

kotlin 复制代码
@Composable
fun UserProfile(userId: String) {
    val user = loadUserFromApi(userId) // ❌ 致命错误!
    Text("用户名:${user.name}")
}

原因: 重组可能由于任何原因(比如一个动画、一次点击)每秒发生 60 次。如果直接在函数体里写,意味着你的 API 请求也会每秒发 60 次。

我们需要一套机制,让逻辑**"只在必要的时候、且以受控的方式"**运行。


二、 核心副作用 API:对症下药

1. LaunchedEffect:协程的避风港

当你需要在进入页面时启动一个协程(如加载数据、倒计时)时,它是首选。

  • 特性:绑定在 Composable 的生命周期上。进入组合时启动,离开时自动取消。
  • Key 机制 :它接收一个 key。只有当 key 变化时,它才会重启。
kotlin 复制代码
LaunchedEffect(userId) { // 只有 userId 变了,才会重新请求
    viewModel.loadData(userId)
}

2. rememberCoroutineScope:事件驱动的协程

如果你想在点击按钮时启动协程(比如显示一个 SnackBar),你不能用 LaunchedEffect(因为它必须在 Composable 内部声明)。

  • 用法 :获取一个与当前 Composable 绑定的 Scope,然后手动启动。
kotlin 复制代码
val scope = rememberCoroutineScope()
Button(onClick = {
    scope.launch { /* 异步操作 */ }
}) { ... }

3. DisposableEffect:有始有终的清理

需要注册监听器、初始化第三方 SDK,并在 UI 销毁时反注册?选它。

  • 强制要求 :必须以 onDispose { ... } 结尾。
kotlin 复制代码
DisposableEffect(lifecycleOwner) {
    val observer = LifecycleEventObserver { ... }
    lifecycleOwner.lifecycle.addObserver(observer)
    
    onDispose {
        lifecycleOwner.lifecycle.removeObserver(observer) // 保证不内存泄漏
    }
}

4. SideEffect:与非 Compose 代码同步

如果你需要将 Compose 内部的状态同步给外部(比如一个由 C++ 维护的渲染器),可以使用它。它在每次成功重组后执行。


三、 高级技巧:处理"长跑"中的变化

rememberUpdatedState:防范闭门羹

想象一个场景:你在 LaunchedEffect 里开了一个 10 秒的定时器,在这 10 秒内,外部传进来的参数变了。默认情况下,协程由于没结束,它拿到的还是 10 秒前的旧值。

  • 方案 :使用 rememberUpdatedState。它能保证协程内部始终引用最新的值,而不需要重启协程。

四、 给开发者的架构建议

  1. 副作用尽量往上提,甚至提进 ViewModel : 在 MVI 或 MVVM 架构中,复杂的业务逻辑(网络、DB)应该在 ViewModel 中处理。Compose 的副作用 API 更多是处理 "UI 相关" 的系统交互(如监听返回键、处理生命周期回调)。
  2. 谨慎选择 KeyLaunchedEffect(true) 意味着只在第一次进入组合时运行。这在处理"进入页面即统计"时很有用。
  3. 永远不要忘记 onDispose : 作为一个资深开发,对内存泄漏的警惕应该是本能。任何使用了 DisposableEffect 的地方,都要反复确认清理逻辑是否闭环。

结语

掌握了副作用 API,你就真正拿到了 Compose 的"控制权"。你不再是被动等待重组的看客,而是能够精确调度逻辑的导演。


下一篇我们将探讨:CompositionLocal:隐式数据传递的利弊与最佳实践。如果你觉得有帮助,欢迎点赞关注,我们在代码上演进,在原理上深耕。

相关推荐
徐小夕2 小时前
jitword 协同文档3.2发布:打造浏览器中最强word编辑器
前端·架构·github
玉宇夕落5 小时前
Harness Engineering 核心四层一:记忆模块的简单学习
架构
BothSavage5 小时前
OpenHarness源码研究-3-codex配置到输出对话
后端·架构
Kapaseker6 小时前
Kotlin Toolchain 0.11 发布:主要是把 Amper 干没了
android·kotlin
三少爷的鞋7 小时前
Android 现代架构不需要事件总线进阶篇
android
杉氧21 小时前
深入理解 Compose 重组机制:快照系统如何驱动 UI 精准刷新?
android·架构·android jetpack
召钱熏1 天前
状态枚举正确≠渲染正确:一个语音按钮的状态机边界修复实录
android·前端
杉氧1 天前
深度解析:Jetpack Compose 核心架构与底层原理 —— 十年安卓老兵的“破茧重生”
android·架构·android jetpack
通玄1 天前
Jetpack Compose 入门系列(七):ViewModel 与界面状态管理
android