目录
- [2. 构建与依赖配置](#2. 构建与依赖配置 "#2-%E6%9E%84%E5%BB%BA%E4%B8%8E%E4%BE%9D%E8%B5%96%E9%85%8D%E7%BD%AE")
- [3. 性能配置](#3. 性能配置 "#3-%E6%80%A7%E8%83%BD%E9%85%8D%E7%BD%AE")
- [4. 稳定性与崩溃治理](#4. 稳定性与崩溃治理 "#4-%E7%A8%B3%E5%AE%9A%E6%80%A7%E4%B8%8E%E5%B4%A9%E6%BA%83%E6%B2%BB%E7%90%86")
- [5. 可测试性配置](#5. 可测试性配置 "#5-%E5%8F%AF%E6%B5%8B%E8%AF%95%E6%80%A7%E9%85%8D%E7%BD%AE")
- [6. 包体积与启动优化](#6. 包体积与启动优化 "#6-%E5%8C%85%E4%BD%93%E7%A7%AF%E4%B8%8E%E5%90%AF%E5%8A%A8%E4%BC%98%E5%8C%96")
- [7. 总结](#7. 总结 "#7-%E6%80%BB%E7%BB%93")
1. 引言
Jetpack Compose 上手容易,但把应用真正推向生产环境时,往往会遇到一系列「demo 里不会遇到」的问题:性能调优、稳定性保障、可测试性、包体积控制、崩溃治理......本文从实际生产经验出发,梳理 Compose 应用从 demo 走向可上线需要关注的关键配置与实践,帮助你少踩坑、快上线。
TL;DR :本文从构建、性能、稳定性、测试、包体积五个维度,梳理 Compose 应用从 demo 走向可上线的关键配置。构建上锁定 Compose 编译器与 Kotlin 版本并开启 R8 混淆;性能上开启强跳过模式、合理使用
remember/derivedStateOf;稳定性上用LaunchedEffect管理副作用并接入监控;测试上为核心交互编写 UI 用例;包体积上用 Baseline Profiles 与按需加载优化启动与体积。
2. 构建与依赖配置
2.1 启用 Compose 编译器
生产环境必须锁定 Compose 编译器版本,并与 Kotlin 版本严格对应:
kotlin
// 根 build.gradle.kts
plugins {
id("org.jetbrains.kotlin.plugin.compose") version "2.0.21"
}
// 模块 build.gradle.kts
android {
buildFeatures {
compose = true
}
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.14"
}
注意:Compose 编译器版本与 Kotlin 版本存在强绑定关系,升级 Kotlin 时必须同步升级编译器插件,否则会出现编译错误或运行时异常。
2.2 开启 R8 混淆与资源收缩
生产构建建议开启代码混淆与资源收缩,显著减小包体积:
kotlin
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
Compose 相关混淆规则通常已由官方库自带,但自定义 Composable 若使用了反射或序列化,需要补充 keep 规则。
2.3 生产环境 vs Demo 环境配置对比
下表汇总了 Compose 项目从 Demo 走向生产时,各项关键配置的差异,便于快速对照落地:
| 配置项 | Demo 环境 | 生产环境 | 说明 |
|---|---|---|---|
| Compose 编译器版本 | 跟随最新版本,灵活升级 | 锁定固定版本,与 Kotlin 严格对应 | 生产环境必须保证编译可复现,避免升级 Kotlin 时编译器不兼容 |
| R8 混淆与资源收缩 | 通常关闭,便于调试 | 开启 isMinifyEnabled 与 isShrinkResources |
显著减小包体积,但需补充自定义 Composable 的 keep 规则 |
| 强跳过模式(Strong Skipping) | 可关闭,便于观察重组 | 开启 enableComposeCompilerStrongSkipping |
减少不必要的重组,对列表、复杂 UI 场景收益明显 |
| Baseline Profiles | 无需配置 | 生成并打包进 APK | 提升冷启动与列表滚动性能,建议在发布前生成 |
| 稳定性监控 | 不接入 | 接入 Crashlytics 或自建上报 | 生产环境需对重组异常、协程取消等 Compose 特有崩溃做专项采集 |
| UI 测试 | 可选,少量冒烟用例 | 核心交互全覆盖,使用稳定 testTag | 保证发布质量,避免回归 |
小结:Demo 追求「跑得快、看得见效果」,生产则追求「稳定、可复现、可观测」。上表所列配置项,建议在发布前逐项核对,避免带着 Demo 配置直接上线。
3. 性能配置
3.1 开启强跳过模式(Strong Skipping)
Compose 编译器 1.5.4+ 支持强跳过模式,可显著减少不必要的重组:
kotlin
// gradle.properties
android.experimental.enableComposeCompilerStrongSkipping=true
该模式让编译器对参数未变化的 Composable 自动跳过重组,对列表、复杂 UI 场景收益明显。
3.2 合理使用 remember 与 derivedStateOf
生产代码中应避免在 Composable 内重复计算昂贵逻辑:
kotlin
@Composable
fun ProductList(products: List<Product>, query: String) {
// 用 derivedStateOf 避免每次重组都重新过滤
val filtered = remember(products, query) {
derivedStateOf { products.filter { it.name.contains(query) } }
}
LazyColumn {
items(filtered.value, key = { it.id }) { product ->
ProductItem(product)
}
}
}
实战:搜索框防抖
搜索场景是防抖的典型应用:用户连续输入时不应每次都触发网络请求,而应在输入停顿后再搜索。下面用 remember 保存防抖状态、LaunchedEffect 配合 delay 实现自动搜索:
kotlin
@Composable
fun SearchScreen(viewModel: SearchViewModel = viewModel()) {
// 1. 用 remember 保存输入框的当前文本(防抖状态)
var query by remember { mutableStateOf("") }
// 2. 保存搜索结果与加载状态
val results by viewModel.results.collectAsState()
val isLoading by viewModel.isLoading.collectAsState()
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
OutlinedTextField(
value = query,
onValueChange = { query = it }, // 只更新本地状态,不直接触发搜索
placeholder = { Text("输入关键词搜索") },
modifier = Modifier.fillMaxWidth()
)
// 3. LaunchedEffect 监听 query 变化,配合 delay 实现防抖
LaunchedEffect(query) {
// 输入停顿 500ms 后才真正发起搜索
delay(500)
viewModel.search(query.trim())
}
// 4. 加载状态展示
if (isLoading) {
CircularProgressIndicator(modifier = Modifier.align(Alignment.CenterHorizontally))
} else if (results.isEmpty()) {
// 5. 空结果处理
Text(
text = "未找到相关结果",
modifier = Modifier.align(Alignment.CenterHorizontally)
)
} else {
LazyColumn {
items(results, key = { it.id }) { item ->
SearchResultItem(item)
}
}
}
}
}
关键点说明:
remember只保存输入文本,不触发搜索,避免每次重组都发起请求;LaunchedEffect(query)在query变化时重启协程,配合delay(500)实现「停顿后才搜索」;- 用户连续输入时,前一个协程会被自动取消,只有最后一次停顿后的输入才会真正发起搜索;
- 加载状态与空结果分别用
CircularProgressIndicator和提示文案展示,交互更完整。
3.3 列表使用 key 提升复用效率
LazyColumn / LazyRow 中务必为 items 指定稳定的 key,避免滚动时 Item 状态错乱:
kotlin
items(items = list, key = { it.id }) { item -> ... }
4. 稳定性与崩溃治理
4.1 配置稳定性监控
生产环境建议接入稳定性监控,例如 Firebase Crashlytics 或自建上报通道,并针对 Compose 特有的崩溃场景(如重组异常、协程取消)做专项采集。
4.2 避免在 Composition 中执行副作用
不要在 Composable 函数体内直接执行网络请求、数据库读写等耗时操作,应使用 LaunchedEffect 或 rememberCoroutineScope 管理副作用:
kotlin
@Composable
fun UserProfile(userId: String) {
var state by remember { mutableStateOf<UiState>(Loading) }
LaunchedEffect(userId) {
state = repository.fetchUser(userId)
}
// 渲染 UI
}
4.3 处理协程取消与资源释放
页面销毁时协程应自动取消,避免内存泄漏:
kotlin
@Composable
fun DetailScreen(viewModel: DetailViewModel = viewModel()) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
// 使用 collectAsStateWithLifecycle 而非 collectAsState,避免后台收集
}
5. 可测试性配置
5.1 引入 Compose UI 测试依赖
kotlin
androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.7.6")
debugImplementation("androidx.compose.ui:ui-test-manifest:1.7.6")
5.2 编写稳定的 UI 测试
生产级项目应为核心交互编写 UI 测试,并设置稳定的测试标签:
kotlin
@Composable
fun LoginButton(onClick: () -> Unit) {
Button(onClick = onClick, modifier = Modifier.testTag("login_button")) {
Text("登录")
}
}
kotlin
@RunWith(AndroidJUnit4::class)
class LoginScreenTest {
@get:Rule
val composeRule = createComposeRule()
@Test
fun clickLogin_showsLoading() {
composeRule.setContent { LoginScreen() }
composeRule.onNodeWithTag("login_button").performClick()
composeRule.onNodeWithText("加载中").assertIsDisplayed()
}
}
6. 包体积与启动优化
6.1 使用 Baseline Profiles
Baseline Profiles 可显著提升冷启动与列表滚动性能:
kotlin
// 模块 build.gradle.kts
plugins {
id("androidx.baselineprofile")
}
生成后放入 src/main/baselineProfiles/ 目录,并在构建配置中引用。
6.2 按需加载与动态特性
对体积较大的功能模块,可考虑使用 Dynamic Feature 按需加载,减少首包体积。
7. 总结
从 demo 到可上线,Compose 项目需要在构建、性能、稳定性、测试、包体积五个维度做系统性配置。核心要点:
- 锁定 Compose 编译器与 Kotlin 版本,开启 R8 混淆;
- 开启强跳过模式,合理使用
remember/derivedStateOf; - 用
LaunchedEffect管理副作用,接入稳定性监控; - 引入 UI 测试并编写核心交互用例;
- 使用 Baseline Profiles 优化启动与滚动性能。
配置到位后,Compose 应用才能在生产环境中稳定、流畅地运行。希望本文能帮你少走弯路,顺利上线。