在 Jetpack Compose 中,DisposableEffect
、LaunchedEffect
和 sideEffect
都是用于处理副作用(Side Effects)的 API,但它们的用途和触发时机不同。以下是它们的核心概念和区别:
1. 副作用(Side Effect)是什么?
副作用是指 Composable 函数中与界面渲染无关的操作,例如:
发起网络请求
订阅/取消订阅数据流
修改外部变量或状态
访问 Android 系统 API(如权限、生命周期)
由于 Composable 函数可能会频繁重组(Recomposition),副作用需要被可控地管理,避免重复执行或资源泄漏。
2. 三者的对比
API | 用途 | 触发时机 | 清理机制 | 适用场景 |
---|---|---|---|---|
LaunchedEffect |
在 Composable 中启动一个协程,执行挂起函数(如异步任务)。 | 当 key 变化时,会取消之前的协程并重新启动。 |
自动取消协程 | 一次性异步操作(如网络请求、动画)。 |
DisposableEffect |
执行需要清理的资源操作(如监听器、订阅)。 | 当 key 变化时,先执行清理逻辑,再重新执行副作用。 |
需手动清理(onDispose ) |
需要释放资源的操作(如广播、RxJava 订阅)。 |
sideEffect |
在每次成功重组后执行的非阻塞操作,不依赖重组是否跳过。 | 每次重组后(即使其他部分跳过重组)。 | 无 | 更新外部状态(如 Analytics 日志、非关键状态同步)。 |
3. 详细说明与代码示例
(1) LaunchedEffect
用途:在 Composable 中安全启动协程,适合异步任务。
特点:
自动取消:当
key
变化或 Composable 退出时,协程会被取消。必须指定
key
(用于控制重启条件)。
@Composable
fun TimerDemo() {
var time by remember { mutableStateOf(0) }
LaunchedEffect(Unit) { // 传入 Unit 表示只启动一次
while (true) {
delay(1000)
time++
}
}
Text("Time: $time")
}
(2) DisposableEffect
用途:管理需要手动释放的资源(如监听器、订阅)。
特点:
必须调用
onDispose
清理资源。key
变化时,先执行onDispose
,再重新初始化。
@Composable fun SensorDemo() { val sensorManager = remember { getSystemService(Context.SENSOR_SERVICE) as SensorManager } var data by remember { mutableStateOf(0f) } DisposableEffect(Unit) { val listener = object : SensorEventListener { override fun onSensorChanged(event: SensorEvent) { data = event.values[0] } override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {} } sensorManager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_NORMAL) onDispose { // 必须清理! sensorManager.unregisterListener(listener) } } Text("Sensor value: $data") }
(3) sideEffect
用途:在重组后执行非阻塞操作,通常用于与 Compose 无关的状态同步。
特点:
每次重组后都会执行(即使其他部分跳过了重组)。
无清理机制,适合轻量级操作。
@Composable
fun AnalyticsButton(clicked: Boolean) {
Button(onClick = { /* ... */ }) {
Text("Click Me")
}
sideEffect {
if (clicked) {
logAnalyticsEvent("ButtonClicked") // 记录日志
}
}
}
4. 关键区别总结
场景 | LaunchedEffect |
DisposableEffect |
sideEffect |
---|---|---|---|
异步任务 | ✅(协程) | ❌ | ❌ |
需要清理的资源 | ❌ | ✅(监听器、订阅) | ❌ |
重组后状态同步 | ❌ | ❌ | ✅(日志、统计) |
自动取消/清理 | ✅(协程) | ✅(需手动 onDispose ) |
❌ |
5. 如何选择?
需要协程? →
LaunchedEffect
需要释放资源? →
DisposableEffect
只需在重组后同步状态? →
sideEffect
通过合理使用这些 API,可以避免副作用导致的性能问题或内存泄漏。