一、基础与原理
-
★ Compose 是什么?和传统 View 有什么区别?
要点:声明式 UI、状态驱动、Kotlin 编译器插件、组合优于继承、无 XML;View 是命令式、手动操作 View 树、findViewById。Compose 通过重组更新 UI。
-
★
@Composable是什么?有什么限制?要点:标记 Compose 函数,编译器会转换并插入
Composer;只能在 Compose 上下文中调用;通常返回 Unit;不应直接写副作用;可返回值但不推荐复杂返回。 -
★ 什么是重组 Recomposition?什么是智能重组?
要点:状态变化后重新执行相关 Composable;Compose 只重组读取了该 State 的最近作用域;可跳过未变化的 Composable。
-
★ Compose 的三个阶段?
要点:Composition → Layout → Drawing。
Composition 读状态会触发重组;Layout 读状态只触发布局;Drawing 读状态只触发重绘。利用
lambda、drawBehind、graphicsLayer可延迟读取优化性能。 -
★ Compose 快照系统 Snapshot 是什么?
要点:
mutableStateOf基于 Snapshot。读取会记录依赖,写入会通知快照系统,Recomposer 调度重组。它让状态变更可观察、可批量。 -
Compose 编译器插件做了什么?
要点:把
@Composable函数转成 IR,加入Composer、Group、changed检查、跳过逻辑,判断函数是否可跳过、可重启。
二、状态管理
-
★
State、MutableState、mutableStateOf、remember关系?要点:
State<T>只读,MutableState<T>可写;mutableStateOf创建状态;remember跨重组缓存对象,避免每次重组重新创建。 -
★
remember和rememberSaveable区别?要点:
remember在配置变更、进程死亡后丢失;rememberSaveable保存到 SavedStateRegistry/Bundle,可恢复;支持基本类型、Parcelable、Serializable,自定义需Saver。 -
★ 什么是状态提升 State Hoisting?
要点:把状态从子组件移到父组件,子组件通过参数接收值、通过回调发出事件。好处:单一数据源、单向数据流、可复用、可测试。
-
★
derivedStateOf有什么用?要点:基于其他 State 派生状态。当依赖频繁变化但结果变化不频繁时,可减少重组。通常写成
remember { derivedStateOf { ... } }。 -
★ 状态读取位置为什么重要?
要点:状态在哪个作用域被读取,写入时就可能只重组该作用域。把读取延迟到 lambda、布局或绘制阶段,可减少重组范围。
-
★ ViewModel 在 Compose 中怎么用?
要点:
viewModel()获取,UI 状态用StateFlow,通过collectAsStateWithLifecycle()收集;事件调用 ViewModel 方法;UI State 推荐不可变 data class。
三、副作用 Effect
-
★
LaunchedEffect作用?要点:进入组合时启动协程;key 改变时取消并重启;离开组合时取消。用于挂起任务,如请求、监听。
-
★
DisposableEffect作用?要点:需要清理的副作用。key 改变或离开组合时执行
onDispose,如注销监听、释放资源。 -
★
SideEffect作用?要点:每次成功重组后执行,非挂起。用于把 Compose 状态发布到非 Compose 管理对象。
-
★
rememberCoroutineScope和LaunchedEffect区别?要点:
rememberCoroutineScope给事件回调手动启动协程,生命周期随组合;LaunchedEffect是声明式自动执行副作用。 -
★
produceState和snapshotFlow区别?要点:
produceState把非 Compose 状态转成State;snapshotFlow把 State 读取转成冷 Flow,状态变化时发射。 -
★
rememberUpdatedState作用?要点:在长期 Effect 中引用最新值或 lambda,而不因值变化重启 Effect。常用于
LaunchedEffect中调用最新回调。 -
为什么不能在 Composable 中直接发请求或启动协程?
要点:Composable 会因重组反复执行,直接写会导致重复请求、泄漏。必须用 Effect API 控制生命周期。
四、性能优化
-
★ 如何优化 Compose 性能?
要点:稳定参数、
key、contentType、remember、derivedStateOf、延迟状态读取、避免反向写状态、优化 LazyColumn、减少不稳定类型、使用 Baseline Profile。 -
★
@Stable和@Immutable区别?要点:
@Immutable表示属性不会变;@Stable表示类型稳定,属性可变但变化会通知 Compose,且 equals 一致。它们帮助编译器判断可跳过。 -
★ 为什么
List在 Compose 中常被认为不稳定?要点:
List是可变接口,Compose 无法保证内容不变。可用ImmutableList或@Immutable包装类。不稳定参数会导致 Composable 无法跳过。 -
★ LazyColumn 如何优化?
要点:提供稳定唯一
key;按 item 类型提供contentType;避免同方向嵌套滚动;用contentPadding;状态用rememberLazyListState。 -
★ Modifier 顺序为什么重要?
要点:Modifier 链有序,影响布局、点击区域、背景、绘制。例如
padding().background()和background().padding()效果不同。 -
什么是重组作用域?
要点:非 inline 的 Composable 通常生成重组作用域,可独立跳过。inline Composable 可能合并作用域。
五、UI、布局、主题
-
Column、Row、Box、ConstraintLayout、Scaffold 区别?
要点:Column/Row 线性布局,Box 层叠,ConstraintLayout 复杂约束,Scaffold 提供 Material 页面结构:TopBar、Snackbar、FAB 等。
-
如何自定义 Layout?
要点:使用
Layout+MeasurePolicy;固有测量IntrinsicSize;SubcomposeLayout用于先测量再组合,如 Lazy 布局。 -
Compose 动画常用 API?
要点:
animate*AsState、AnimatedVisibility、Crossfade、updateTransition、rememberInfiniteTransition。 -
CompositionLocal 是什么?
要点:隐式向下传递数据,如
MaterialTheme、LocalContext。compositionLocalOf跟踪读取,staticCompositionLocalOf不跟踪读取。不要滥用。
六、互操作、架构、测试
-
★ Compose 和 View 如何互操作?
要点:Compose 中用
AndroidView包装 View;XML/View 中用ComposeView嵌入 Compose;注意ViewCompositionStrategy生命周期。 -
★ Navigation Compose 怎么用?
要点:
rememberNavController、NavHost、composable、navigate、参数传递。类型安全导航可用 Kotlin Serialization 的@Serializable路由。 -
★ Hilt 在 Compose 中怎么用?
要点:
@HiltViewModel标记 ViewModel,Composable 中用hiltViewModel()获取。 -
★ Compose 测试怎么做?
要点:
createComposeRule/createAndroidComposeRule;setContent;onNodeWithText、onNodeWithTag;performClick、assertIsDisplayed、waitUntil。 -
Compose 中如何处理生命周期?
要点:Flow 用
collectAsStateWithLifecycle();副作用可用LifecycleEventEffect、LifecycleResumeEffect;Activity Result 用rememberLauncherForActivityResult。 -
Paging 在 Compose 中怎么用?
要点:
collectAsLazyPagingItems()配合LazyColumn,处理 loading、error、append 状态。
高频追问
-
为什么状态变化会触发重组?
-
如何减少不必要的重组?
-
remember、rememberSaveable、ViewModel 分别适合存什么? -
LaunchedEffect、DisposableEffect、SideEffect区别? -
什么是稳定类型?为什么要稳定?
-
derivedStateOf和普通remember计算区别? -
Modifier 顺序为什么会影响点击和背景?
-
LazyColumn 的
key和contentType有什么用? -
Compose 性能排查工具?
答:Layout Inspector 重组计数、Compose Compiler Metrics、Profiler、Baseline Profile。