副作用 (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:隐式数据传递的利弊与最佳实践。如果你觉得有帮助,欢迎点赞关注,我们在代码上演进,在原理上深耕。

相关推荐
YM52e2 小时前
鸿蒙Flutter Padding内边距:EdgeInsets详解
android·学习·flutter·华为·harmonyos·鸿蒙
Rex叶然6 小时前
ScreenMatch适配安卓屏幕脚本,用于老项目
android·screenmatch·安卓屏幕适配
阿pin6 小时前
Android随笔-Activity启动流程深度分析
android·activity启动流程
zzq77977 小时前
加固包闪退四象限定位:targetSdk 与保护策略实战解析
android·安全·安卓·安全架构
童谣18 小时前
越华环保集团市级统筹申报数字化中台:政策校验与减排仿真一体化架构
数据库·架构
我命由我123458 小时前
Android 开发问题:Cannot resolve method ‘repeat‘ in ‘String‘
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
光头闪亮亮9 小时前
Fyne ( go跨平台GUI )项目实战-scanner摄像头扫码组件开发技术详解
android·go
Android打工仔9 小时前
Continuation 到底是谁创建的?
android·kotlin
DeepAgent9 小时前
AI Agent 工程实践(17):Agent 为什么需要可观测性(Observability)?
android·llm·agent
提笔了无痕10 小时前
MySQL SQL 从 EXPLAIN 到索引优化,搞懂 SQL 为什么慢
android·sql·mysql