当前位置: 首页 > news >正文

Jetpack Compose 与 ViewModel 的完美结合

在 Jetpack Compose 中,ViewModel 是管理 UI 状态和业务逻辑的核心组件。它与 Compose 的响应式编程模型完美契合,下面是详细的整合方法和最佳实践。

基本集成方式

1. 添加必要依赖

dependencies {implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2'implementation 'androidx.compose.runtime:runtime-livedata:1.5.4'
}

2. 创建 ViewModel

class CounterViewModel : ViewModel() {// 使用 StateFlow 管理状态private val _count = MutableStateFlow(0)val count: StateFlow<Int> = _count.asStateFlow()fun increment() {_count.value += 1}fun reset() {_count.value = 0}
}

3. 在 Compose 中使用 ViewModel

@Composable
fun CounterScreen() {// 获取 ViewModel 实例val viewModel: CounterViewModel = viewModel()// 将 StateFlow 转换为 Compose 状态val count by viewModel.count.collectAsState()Column(modifier = Modifier.fillMaxSize(),verticalArrangement = Arrangement.Center,horizontalAlignment = Alignment.CenterHorizontally) {Text(text = "计数: $count", style = MaterialTheme.typography.h4)Spacer(modifier = Modifier.height(16.dp))Button(onClick = { viewModel.increment() }) {Text("增加")}Button(onClick = { viewModel.reset() },modifier = Modifier.padding(top = 8.dp)) {Text("重置")}}
}

进阶用法

1. 处理复杂状态

class UserViewModel : ViewModel() {// 使用密封类管理多种状态private val _userState = MutableStateFlow<UserState>(UserState.Loading)val userState: StateFlow<UserState> = _userState.asStateFlow()init {loadUserData()}private fun loadUserData() {viewModelScope.launch {_userState.value = UserState.Loadingtry {val user = userRepository.getUser()_userState.value = UserState.Success(user)} catch (e: Exception) {_userState.value = UserState.Error(e.message ?: "未知错误")}}}fun retry() {loadUserData()}
}sealed class UserState {object Loading : UserState()data class Success(val user: User) : UserState()data class Error(val message: String) : UserState()
}

2. 在 Compose 中处理复杂状态

@Composable
fun UserProfileScreen() {val viewModel: UserViewModel = viewModel()val userState by viewModel.userState.collectAsState()when (val state = userState) {is UserState.Loading -> {Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {CircularProgressIndicator()}}is UserState.Success -> {UserDetailsView(user = state.user)}is UserState.Error -> {ErrorView(message = state.message,onRetry = { viewModel.retry() })}}
}

3. 使用 Hilt 依赖注入

// 添加 Hilt 依赖
implementation 'com.google.dagger:hilt-android:2.48.1'
kapt 'com.google.dagger:hilt-compiler:2.48.1'
implementation 'androidx.hilt:hilt-navigation-compose:1.1.0'

// 使用 @HiltViewModel 注解
@HiltViewModel
class SettingsViewModel @Inject constructor(private val settingsRepository: SettingsRepository
) : ViewModel() {private val _theme = MutableStateFlow(AppTheme.SYSTEM)val theme: StateFlow<AppTheme> = _theme.asStateFlow()init {loadSettings()}private fun loadSettings() {viewModelScope.launch {_theme.value = settingsRepository.getTheme()}}fun setTheme(theme: AppTheme) {viewModelScope.launch {settingsRepository.saveTheme(theme)_theme.value = theme}}
}// 在 Compose 中使用
@Composable
fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel()) {val theme by viewModel.theme.collectAsState()// UI 实现...
}

最佳实践

1. 状态管理原则

原则说明
单一数据源ViewModel 应是 UI 状态的唯一真实来源
单向数据流UI 事件 → ViewModel → 更新状态 → UI 刷新
状态最小化只暴露 UI 需要的最小状态
不可变性对外暴露的状态应是不可变的(使用 StateFlow 或 LiveData)

2. 状态提升模式

@Composable
fun UserInputSection(username: String,onUsernameChange: (String) -> Unit,password: String,onPasswordChange: (String) -> Unit,onLogin: () -> Unit
) {Column {OutlinedTextField(value = username,onValueChange = onUsernameChange,label = { Text("用户名") })OutlinedTextField(value = password,onValueChange = onPasswordChange,label = { Text("密码") },visualTransformation = PasswordVisualTransformation())Button(onClick = onLogin) {Text("登录")}}
}@Composable
fun LoginScreen(viewModel: LoginViewModel = viewModel()) {val state by viewModel.uiState.collectAsState()UserInputSection(username = state.username,onUsernameChange = viewModel::updateUsername,password = state.password,onPasswordChange = viewModel::updatePassword,onLogin = viewModel::login)if (state.isLoading) {CircularProgressIndicator()}
}

3. 处理副作用

@Composable
fun LoginScreen(viewModel: LoginViewModel = viewModel()) {val state by viewModel.uiState.collectAsState()// 处理导航副作用val navController = rememberNavController()LaunchedEffect(state.isLoginSuccess) {if (state.isLoginSuccess) {navController.navigate("home")}}// 处理错误提示val context = LocalContext.currentLaunchedEffect(state.errorMessage) {state.errorMessage?.let {Toast.makeText(context, it, Toast.LENGTH_SHORT).show()viewModel.errorShown()}}// UI 实现...
}

4. ViewModel 生命周期管理

@Composable
fun MyApp() {val navController = rememberNavController()val backstackEntry by navController.currentBackStackEntryAsState()// 获取当前路由的 ViewModelval currentRoute = backstackEntry?.destination?.routeval viewModelStoreOwner = backstackEntry ?: LocalViewModelStoreOwner.currentScaffold { innerPadding ->NavHost(navController = navController,startDestination = "home",modifier = Modifier.padding(innerPadding)) {composable("home") {// HomeScreen 使用当前导航条目的 ViewModelval homeViewModel: HomeViewModel = viewModel(viewModelStoreOwner)HomeScreen(viewModel = homeViewModel)}composable("profile") {ProfileScreen()}}}
}

常见问题解决方案

1. 状态持久化

class CounterViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {companion object {private const val COUNT_KEY = "count"}private val _count = MutableStateFlow(savedStateHandle.get<Int>(COUNT_KEY) ?: 0)val count: StateFlow<Int> = _count.asStateFlow()init {// 保存状态到 SavedStateHandleviewModelScope.launch {_count.collect { count ->savedStateHandle[COUNT_KEY] = count}}}fun increment() {_count.value += 1}
}
 

2. 性能优化

class ProductViewModel : ViewModel() {// 使用 derivedStateOf 优化派生状态private val _products = MutableStateFlow(emptyList<Product>())val products: StateFlow<List<Product>> = _products.asStateFlow()val favoriteProducts: StateFlow<List<Product>> = _products.derivedStateFlow { products.value.filter { it.isFavorite }}// 使用 flatMapLatest 处理异步操作val searchQuery = MutableStateFlow("")val searchResults = searchQuery.debounce(300) // 防抖 300ms.flatMapLatest { query ->if (query.isEmpty()) {flowOf(emptyList())} else {productRepository.searchProducts(query)}}.stateIn(scope = viewModelScope,started = SharingStarted.WhileSubscribed(5000),initialValue = emptyList())
}
 

3. 测试策略

class CounterViewModelTest {@Testfun `increment should increase count by 1`() = runTest {// 创建 ViewModelval viewModel = CounterViewModel()// 初始状态验证assertEquals(0, viewModel.count.value)// 执行操作viewModel.increment()// 验证结果assertEquals(1, viewModel.count.value)}
}@Composable
fun CounterPreview() {// 预览模式使用假数据val fakeViewModel = CounterViewModel().apply {_count.value = 5}CounterScreen(viewModel = fakeViewModel)
}
 

总结

Jetpack Compose 与 ViewModel 的结合提供了强大的状态管理能力:

  1. ViewModel 作为状态容器:管理 UI 状态和业务逻辑

  2. 响应式状态更新:使用 StateFlow/LiveData + collectAsState 实现自动刷新

  3. 单向数据流:确保状态变化的可预测性和可维护性

  4. 生命周期感知:自动处理配置更改和资源清理

  5. 依赖注入支持:通过 Hilt 简化依赖管理

遵循这些模式和实践,你可以构建出结构清晰、可维护且高效响应的 Compose 应用。

相关文章:

  • Vite中定义@软链接
  • MacBook pro 修改Homebrew 为中国源
  • UI 自动化测试工具推荐
  • 基于鸿蒙(HarmonyOS5)的打车小程序
  • 时序数据库IoTDB在工业物联网时序数据管理中的应用
  • 【AI】从0开始玩转混元3D⼤模型,如何让一张静态实物图片一键转为3D实物图,大模型都表示服了,超级简单易上手,快来试试!
  • 音乐“穿梭机”AudioRelay,让你的音频“无缝对接”
  • STM32使用旋转电位器自制调光灯
  • Android多媒体——音/视频数据播放(十八)
  • 视频质量测试点
  • c语言tips-结构体数组 VS 链表宏:`list_for_each_entry` 的优势与局限对比分析
  • 配置 macOS 上的 Ruby 开发环境
  • python爬虫——气象数据爬取
  • 机器学习与深度学习16-概率论和统计学01
  • WEB3全栈开发——面试专业技能点P5中间件
  • CppCon 2015 学习:Simple, Extensible Pattern Matching in C++14
  • 中文分词双向匹配
  • .Net 优秀框架 ABP全面详解
  • 【JMeter】接口断言
  • “一张网,万般用”——聊聊网络虚拟化到底怎么实现的
  • 扶贫基金会网站建设是哪家公司/江门百度seo公司
  • 网站文章模板/千锋教育可靠吗
  • 网站建设的安全可行性/万网域名官网
  • 微信防红短链接生成/南宁企业官网seo
  • 中小企业做网站/石家庄网站建设公司
  • 做网站编程要学什么/站长之家权重查询