jetpack compose 副作用 SideEffect

SideEffect 用于将 Compose 内部状态安全地同步到外部非 Compose 世界。执行时机Composition 成功提交后、下一次重组之前。如果组合失败,SideEffect 不会执行。

场景 1:将 Compose 状态同步到外部对象

kotlin 复制代码
@Composable
fun ChatScreen(viewModel: ChatViewModel) {
    val messages by viewModel.messages.collectAsState()
    val listState = rememberLazyListState()
    
    SideEffect {
        // 把滚动位置同步给 ViewModel 做持久化
        viewModel.saveScrollPosition(listState.firstVisibleItemIndex)
    }
}

场景 2:分析埋点

kotlin 复制代码
@Composable
fun ProductPage(product: Product, tracker: AnalyticsTracker) {
    SideEffect {
        tracker.logEvent("product_view", mapOf("id" to product.id))
    }
}

注意事项 1、不要在 SideEffect 里修改 Compose State --- 会导致无限重组循环 2、适合"读"操作,不适合"写"操作 --- 它是同步出去,不是触发变化 3、如果需要异步操作,用 LaunchedEffect 而不是 SideEffect

源码

kotlin 复制代码
// 1. SideEffect.kt 
@Composable
fun SideEffect(effect: () -> Unit) {
    currentComposer.recordSideEffect(effect)
}

和 snapshotFlow 的对比

对比项 SideEffect snapshotFlow
登记方式 recordSideEffect() → changes 列表 Snapshot.registerApplyObserver()
执行时机 Composition commit 成功 Global Snapshot apply 成功
失败处理 组合失败 → 丢弃 Snapshot apply 成功才回调
底层共同点 都依赖"记录→提交"模式,不是立即执行 都依赖"记录→提交"模式,不是立即执行

Snapshot 状态管理层 和 Composer UI 构建层

scss 复制代码
Composition 层 (构建 UI 树)
    │
    │  依赖
    ▼
Snapshot 层 (管理状态读写)

Snapshot apply 发生在底层,Composition commit 发生在上层。一个改了状态值,一个改了 UI 树。 Snapshot apply 只是把数据标记为已确认,但 UI 树不一定刷新:

less 复制代码
// 场景:快速连续点击
button.onClick {
    count++  // → 触发 Snapshot apply(数据层)
    count++  // → 又触发一次 Snapshot apply
}
// 但 Composition 只会在下一帧跑一次重组
// → 两次 Snapshot apply,一次 Composition commit
对比项 Snapshot apply Composition commit
改了啥 State 值(变量层面) UI 节点树(LayoutNode 层面)
所在的层 Snapshot 系统 Composer 系统
触发者 MutableSnapshot.apply() 帧回调 + Composer
失败怎么办 几乎不会失败(纯数据合并) 可能失败(异常/中断 → 丢弃整棵树)
观察者 registerApplyObserver recordSideEffect
典型案例 snapshotFlow 在此回调 SideEffect 在此回调
相关推荐
alexhilton4 天前
提升 Android Compose 页面的流畅度
android·kotlin·android jetpack
MiyamuraMiyako4 天前
Compose LazyRow 实现尾项露出与少量数据均分
android·android jetpack
传奇开心果编程5 天前
【Jetpack Compose娓娓道来】第29课:状态容器与UI事件建模——把“散装状态”变成“可穷举的契约”
android·kotlin·移动开发·android jetpack·ui框架·开发范式·声明式ui
alexhilton9 天前
用Konsist与Lint强制执行设计系统契约
android·kotlin·android jetpack
传奇开心果编程10 天前
组合优先开发范式:从汉字造字原理到声明式UI组件组合思想
android·flutter·ios·swiftui·移动开发·harmonyos·android jetpack
JMchen11 天前
【Jetpack 内核实战 08 】:组件整合——DataStore + Room 构建离线优先架构
架构·kotlin·android jetpack
Android打工仔11 天前
Compose 中何时该用 padding,何时该用 offset
android·android jetpack
hai_android17 天前
Android 设备锁定:Kiosk Mode 实战
android·java·android jetpack
stevenzqzq17 天前
Android Jetpack Compose 列表开发指南:为什么你不需要再为底部 Item 写额外间距
android·android jetpack