为什么会有副作用设计
Compose 副作用 API 的核心设计目的,是在声明式UI框架中,为那些必须在“组合生命周期”内执行、但又会影响“组合之外世界”的操作,提供一套安全、可控的执行机制。
之所以需要这套专门的机制,根源在于 Compose 的 “重组” 特性。@Composable 函数会因状态变化而频繁、不可预测地被多次调用。如果直接将网络请求、数据库访问等操作写在函数体内,这些操作会被重复执行,导致资源浪费、程序行为错乱甚至崩溃。副作用 API 就是为解决此矛盾而生的“锚点”。
📌 主要副作用 API 的设计目的与对比
为了方便你理解,这里将几个核心 API 的差异总结如下:
• LaunchedEffect
- 核心设计目的:在组合内安全地执行挂起函数和异步任务。
- 关键特性/生命周期:随组合进入启动协程,随组合退出或键(Key) 变化时取消并重启。
- 典型场景:触发一次性事件(如显示Snackbar)、发起网络请求、收集Flow数据流。
• DisposableEffect
- 核心设计目的:管理需要配对清理的资源或监听。
- 关键特性/生命周期:提供
onDispose块,在组合退出或键变化时必定执行清理。 - 典型场景:注册/注销广播接收器、监听器,绑定/解绑系统服务(如摄像头、传感器)。
• SideEffect
• rememberCoroutineScope
- 核心设计目的:获取一个与组合生命周期绑定的协程作用域,用于在组合外(如回调中)启动协程。
- 关键特性/生命周期:作用域在组合退出时自动取消所有子协程。
- 典型场景:在按钮的
onClick回调中发起异步操作。
• produceState
-
(tips:似乎对于最近的项目有用处。)
💡 如何选择正确的副作用 API?
你可以遵循这个决策流程:
-
是否需要清理资源? (如监听器、广播)
-
操作是否是异步或挂起函数? (如网络请求、延时)
-
是否仅在重组后同步更新外部状态? (如埋点、同步UI)
⚠️ 使用注意事项与最佳实践
- 避免无限循环:不要在副作用内部修改触发该副作用的状态键(Key) ,否则会导致重组循环。
- 键(Key)的妙用:
LaunchedEffect、DisposableEffect等都依赖键的变化来决定是否重启。使用Unit或true等常量作为键,可使副作用只在整个组合生命周期内执行一次。 - 轻量使用 SideEffect:
SideEffect在每次重组后都会执行,切勿在其中放置耗时操作,以免导致界面卡顿。 - 理解生命周期:Compose 副作用 API 感知的是 “组合生命周期” ,而非 Android 组件(Activity/Fragment)的生命周期。两者可能不同步,如需感知后者,应在副作用内部手动处理。
因为AI,所以Deepseek