Jetpack Compose之“副作用”的讲解

Compose附带效应的核心概念

在 Jetpack Compose 中,"附带效应"(Side Effects)是一个核心概念,用于处理 "在 Compose 的声明式 UI 之外发生的、与 UI 状态同步但不直接参与 UI 渲染的操作"。

📌 一句话定义:

附带效应 = 在 Composable 函数中执行的、会影响外部世界(如启动协程、修改非 Compose 状态、调用系统 API)的操作。

为什么需要"附带效应"?

Compose 的核心是纯函数 + 响应式状态驱动 UI 。理想情况下,@Composable 函数应该是:

  • 无副作用(Pure):只根据输入状态生成 UI,不修改外部状态。
  • 可重入(Re-entrant):可能被多次、任意顺序调用。

但现实开发中,我们经常需要:

  • 启动网络请求
  • 记录埋点日志
  • 修改 ViewModel 中的状态
  • 请求权限
  • 播放音效
  • 跳转页面

这些操作不能随意执行 (比如每次重组都调一次API就炸了),必须 精确控制执行时机和次数

Jetpack Compose 提供的主要附带效应 API

🧪 常见使用场景详解

1️⃣ LaunchedEffect ------ 最常用!

在协程中执行逻辑,仅当 key 变化时重新启动。

✅ 场景:页面加载数据

kotlin 复制代码
@Composable
fun UserProfileScreen(userId: String, viewModel: UserViewModel) {
    // 当 userId 变化时,重新加载用户数据
    LaunchedEffect(userId) {
        viewModel.loadUser(userId)
    }

    val user by viewModel.userState.collectAsState()
    // ... 显示 UI
}

✅ 场景:收集 Flow(替代 LiveData)

kotlin 复制代码
LaunchedEffect(Unit) {
    viewModel.uiState.collect { state ->
        when (state) {
            is Success -> showSuccess(state.data)
            is Error -> showError(state.message)
        }
    }
}

⚠️ 注意:不要写 LaunchedEffect(true),应使用 Unit 或具体 key。

2️⃣ DisposableEffect ------ 需要清理的资源

类似 android 中Activity的生命周期 onResume / onDestroy,进入时执行,离开时清理。

✅ 场景:注册广播接收器

kotlin 复制代码
@Composable
fun BatteryStatusScreen() {
    var batteryLevel by remember { mutableStateOf(0f) }

    DisposableEffect(Unit) {
        val receiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                batteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0).toFloat()
            }
        }

        context.registerReceiver(receiver, IntentFilter(Intent.ACTION_BATTERY_CHANGED))

        // 返回清理逻辑
        onDispose {
            context.unregisterReceiver(receiver)
        }
    }

    Text("Battery: ${batteryLevel}%")
}

✅ 场景:连接蓝牙设备、开启摄像头等。

3️⃣ rememberUpdatedState ------ 解决"闭包捕获旧值"问题

kotlin 复制代码
@Composable
fun TimerScreen(onTick: () -> Unit) {
    val currentOnTick by rememberUpdatedState(onTick)

    LaunchedEffect(Unit) {
        while (true) {
            delay(1000)
            currentOnTick() // ← 总是最新版本
        }
    }
}

4️⃣ SideEffect ------ 每次重组都执行(谨慎!)

用于 将 Compose 状态同步到非 Compose 系统。

✅ 场景:设置非 Compose 组件的状态(如 Fragment)

kotlin 复制代码
@Composable
fun SyncWithFragment(title: String) {
    SideEffect {
        // 每次 title 变化,都更新 Activity 标题
        (LocalContext.current as? AppCompatActivity)?.supportActionBar?.title = title
    }
}

⚠️ 不要在这里做耗时操作或修改 Compose State!会导致无限重组。

5️⃣ produceState ------ 封装异步数据加载

将任意异步操作(如 Retrofit 调用)转换为 State。

kotlin 复制代码
@Composable
fun loadUserProfile(userId: String): State<Resource<User>> {
    return produceState(initialValue = Resource.Loading()) {
        try {
            val user = api.getUser(userId)
            value = Resource.Success(user)
        } catch (e: Exception) {
            value = Resource.Error(e.message ?: "Unknown error")
        }
    }
}

// 使用
val userState by loadUserProfile("123")

🚫 常见错误 & 最佳实践

✅ 总结:什么时候用附带效应?

💡 核心思想:

附带效应 = "受控的副作用"。

它让 Compose 在保持声明式的同时,安全地与命令式世界交互。

相关推荐
游戏开发爱好者836 分钟前
日常开发与测试的 App 测试方法、查看设备状态、实时日志、应用数据
android·ios·小程序·https·uni-app·iphone·webview
王码码203541 分钟前
Flutter for OpenHarmony 实战之基础组件:第三十一篇 Chip 系列组件 — 灵活的标签化交互
android·flutter·交互·harmonyos
黑码哥1 小时前
ViewHolder设计模式深度剖析:iOS开发者掌握Android列表性能优化的实战指南
android·ios·性能优化·跨平台开发·viewholder
亓才孓1 小时前
[JDBC]元数据
android
独行soc1 小时前
2026年渗透测试面试题总结-17(题目+回答)
android·网络·安全·web安全·渗透测试·安全狮
金融RPA机器人丨实在智能1 小时前
Android Studio开发App项目进入AI深水区:实在智能Agent引领无代码交互革命
android·人工智能·ai·android studio
科技块儿1 小时前
利用IP查询在智慧城市交通信号系统中的应用探索
android·tcp/ip·智慧城市
独行soc2 小时前
2026年渗透测试面试题总结-18(题目+回答)
android·网络·安全·web安全·渗透测试·安全狮
王码码20352 小时前
Flutter for OpenHarmony 实战之基础组件:第二十七篇 BottomSheet — 动态底部弹窗与底部栏菜单
android·flutter·harmonyos
2501_915106322 小时前
app 上架过程,安装包准备、证书与描述文件管理、安装测试、上传
android·ios·小程序·https·uni-app·iphone·webview