Android Jetpack Compose 专家
Android Jetpack Compose 专家
概述
本指南旨在帮助开发者使用 Jetpack Compose 构建生产级 Android 应用程序。涵盖的内容包括架构模式、基于 ViewModel 的状态管理、类型安全的导航以及性能优化技术。
适用场景
- 启动使用 Jetpack Compose 的新 Android 项目时。
- 将传统的 XML 布局迁移到 Compose 时。
- 实现复杂的 UI 状态管理和副作用(Side Effects)时。
- 优化 Compose 性能(如重组次数、稳定性)时。
- 配置类型安全的导航(Navigation)时。
分步指南
1. 项目设置与依赖
确保你的 libs.versions.toml 包含了必要的 Compose BOM 和相关库。
[versions]
composeBom = "2024.02.01"
activityCompose = "1.8.2"
[libraries]
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
2. 状态管理模式 (MVI/MVVM)
使用 ViewModel 配合 StateFlow 来暴露 UI 状态。避免直接暴露 MutableStateFlow。
// UI 状态定义
data class UserUiState(
val isLoading: Boolean = false,
val user: User? = null,
val error: String? = null
)
// ViewModel
class UserViewModel @Inject constructor(
private val userRepository: UserRepository
) : ViewModel() {
private val _uiState = MutableStateFlow(UserUiState())
val uiState: StateFlow<UserUiState> = _uiState.asStateFlow()
fun loadUser() {
viewModelScope.launch {
_uiState.update { it.copy(isLoading = true) }
try {
val user = userRepository.getUser()
_uiState.update { it.copy(user = user, isLoading = false) }
} catch (e: Exception) {
_uiState.update { it.copy(error = e.message, isLoading = false) }
}
}
}
}
3. 创建 Screen Composable
在“Screen”级 Composable 中消费状态,并将数据向下传递给无状态(Stateless)组件。
@Composable
fun UserScreen(
viewModel: UserViewModel = hiltViewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
UserContent(
uiState = uiState,
onRetry = viewModel::loadUser
)
}
@Composable
fun UserContent(
uiState: UserUiState,
onRetry: () -> Unit
) {
Scaffold { padding ->
Box(modifier = Modifier.padding(padding)) {
when {
uiState.isLoading -> CircularProgressIndicator()
uiState.error != null -> ErrorView(uiState.error, onRetry)
uiState.user != null -> UserProfile(uiState.user)
}
}
}
}
示例
示例 1:类型安全导航
使用最新的 Navigation Compose 类型安全特性
(在最新版本中可用)。
// 定义目的地
@Serializable
object Home
@Serializable
data class Profile(val userId: String)
// 配置 NavHost
@Composable
fun AppNavHost(navController: NavHostController) {
NavHost(navController, startDestination = Home) {
composable<Home> {
HomeScreen(onNavigateToProfile = { id ->
navController.navigate(Profile(userId = id))
})
}
composable<Profile> { backStackEntry ->
val profile: Profile = backStackEntry.toRoute()
ProfileScreen(userId = profile.userId)
}
}
}
最佳实践
- ✅ 建议: 使用
remember和derivedStateOf来减少重组期间不必要的计算。
- ✅ 建议: 如果 UI 状态中使用的 data class 包含
List或其他不稳定类型,请将其标记为@Immutable或@Stable,以启用智能重组跳过。
- ✅ 建议: 对于由状态变化触发的一次性副作用(如显示 Snackbar),请使用
LaunchedEffect。
- ❌ 避免: 在 Composable 函数体中直接执行昂贵的操作(如对列表进行排序)而没有使用
remember。
- ❌ 避免: 将
ViewModel实例向下传递给子组件。仅传递数据(状态)和 lambda 回调(事件)。
故障排除
问题: 无限重组循环。
解决方案: 检查是否在 composition 过程中创建了新的对象实例(如 List 或 Modifier)而未使用 remember,或者是否在 composition 阶段而非副作用或回调中更新了状态。使用 Layout Inspector 来调试重组次数。
局限性
- 仅在任务明确符合上述范围时使用此技能。
- 不要将输出结果视为环境特定验证、测试或专家评审的替代方案。
- 如果缺少必要的输入、权限、安全边界或成功标准,请停止并请求澄清。