Jetpack Compose 技术说明:LaunchedEffect 的执行机制与实践
1. 核心概念
LaunchedEffect 是 Compose 中最常用的副作用(Side-Effect)API。它允许开发者在组件的生命周期内启动一个协程,用于处理非 UI 渲染相关的逻辑(如:页面跳转、网络请求、状态重置等)。
2. 执行时机
LaunchedEffect 的生命周期紧随其在 Composition(UI 树) 中的状态。
2.1 初始挂载 (Initial Mount)
当组件第一次进入 UI 树(即页面开始渲染)时,LaunchedEffect 内部的代码块会立即执行。
- 注意:它是在 Composition 完成后 才在协程中运行的。因此,它能安全地操作如 PagerState 或 ScrollState 等已经绑定到 UI 的状态对象。
2.2 Key 的变更 (Key Change)
LaunchedEffect(key) 依赖其参数 key 来决定是否重新运行:
- 对比机制:使用 equals() 比较新旧 Key。
- 触发动作:如果 Key 发生变化,它会立即取消当前的协程,并重新启动一个新的协程。
- 固定 Key:使用 LaunchedEffect(Unit) 意味着 Key 永远不变,逻辑在组件整个生命周期内仅执行一次。
2.3 销毁与重建 (Dispose & Re-enter)
如果组件因为 if/when 条件或导航被移出 UI 树,随后又重新进入:
- Compose 会将其视为全新实例。
- LaunchedEffect 会重新触发"初始挂载"逻辑。
3. 典型应用:搜索结果页的单次跳转
在"带参数跳转到搜索结果页"的场景中,利用 LaunchedEffect(Unit) 可以完美实现:初始进入时跳转,后续用户操作不干扰。
代码示例
kotlin
@Composable
fun SearchResultPage(initialTab: MutableState<Int>) {
val pagerState = rememberPagerState(initialPage = 0) { 5 }
// 使用 Unit 作为 Key,确保逻辑在进入结果页时仅运行一次
LaunchedEffect(Unit) {
val target = initialTab.value
if (target != 0) {
// 1. 发出跳转指令
pagerState.scrollToPage(target)
// 2. 消费并重置参数
initialTab.value = 0
Log.d("Track", "已完成初始跳转并重置参数")
}
}
}
4. 与普通渲染逻辑的区别
| 维度 | 普通渲染代码 (Function Body) | LaunchedEffect 代码块 |
|---|---|---|
| 执行频率 | 每次重组(Recomposition)都会执行 | 仅在 Key 变化或初次挂载时执行 |
| 执行环境 | UI 线程,不可调用挂起函数 | 协程作用域,支持调用挂起函数 (如 scrollToPage) |
| 主要用途 | 描述 UI 结构 | 处理动画启动、数据加载、埋点上报 |
5. 开发建议
- 谨慎选择 Key:如果逻辑仅需在页面加载时运行,使用 Unit;如果需要监听某个值的变化(如搜索词变了要刷新列表),请将该值作为 key。
- 避免在 Effect 中修改频繁变动的 State:除非有 if 条件限制(如上述的重置为 0),否则在 Effect 中修改 State 可能导致不必要的循环重组。
- 使用 rememberUpdatedState:如果 Effect 内部需要引用最新的参数但又不希望 Effect 重新启动,请配合 rememberUpdatedState 使用。
总结:LaunchedEffect 是连接声明式 UI 与命令式业务逻辑(如 scrollToPage)的桥梁,通过合理设置 Key,可以精确控制业务逻辑的触发频率,实现"单次消费"等复杂交互需求。