核心组件关系图
[View] -- 观察 --> [ViewModel] -- 操作 --> [Repository]| |
Compose UI StateFlow/LiveData| |
用户交互事件 Room/Retrofit| |
[ViewModel] <-- 数据更新 -- [Data Sources]
开发流程详解
-
数据层 (Model)
- 实体类定义:Room 实体或数据类
- Repository 模式:
- 统一管理数据源(Room/Retrofit/文件)
- 提供干净的 API 给 ViewModel
-
ViewModel 层
- 使用
androidx.lifecycle
组件 - 核心职责:
- 持有 UI 状态(
StateFlow
/MutableStateFlow
) - 处理业务逻辑
- 暴露不可变状态给 UI 层
- 持有 UI 状态(
- 使用协程管理异步操作
- 使用
-
UI 层 (Compose)
- 基于状态的声明式编程
- 关键组件:
@Composable
函数构建 UIremember
管理组件状态ViewModel
通过viewModel()
获取
- 状态管理:
- 通过
collectAsState()
观察 ViewModel 状态 - 用户事件通过 ViewModel 方法回调
- 通过
-
数据绑定
- 单向数据流:
-
依赖管理
- 使用 Hilt 实现依赖注入:
@HiltViewModel
注解 ViewModel@Inject
构造函数注入 Repository
- 使用 Hilt 实现依赖注入:
Todo 应用最佳实践实现
1. 添加依赖 (app/build.gradle)
dependencies {// Composeimplementation 'androidx.activity:activity-compose:1.8.0'implementation "androidx.compose.material3:material3:1.1.2"implementation "androidx.compose.ui:ui-tooling-preview:1.5.4"debugImplementation "androidx.compose.ui:ui-tooling:1.5.4"// Lifecycleimplementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2"implementation "androidx.lifecycle:lifecycle-runtime-compose:2.6.2"// Roomimplementation "androidx.room:room-runtime:2.6.0"implementation "androidx.room:room-ktx:2.6.0"kapt "androidx.room:room-compiler:2.6.0"// Hiltimplementation "com.google.dagger:hilt-android:2.48"kapt "com.google.dagger:hilt-compiler:2.48"
}
2. 数据模型
@Entity(tableName = "todos")
data class Todo(@PrimaryKey(autoGenerate = true) val id: Long = 0,val title: String,val description: String = "",val isCompleted: Boolean = false,val createdAt: LocalDateTime = LocalDateTime.now()
)
3. Repository 实现
interface TodoRepository {fun getAllTodos(): Flow<List<Todo>>suspend fun addTodo(todo: Todo)suspend fun updateTodo(todo: Todo)suspend fun deleteTodo(todo: Todo)
}class TodoRepositoryImpl @Inject constructor(private val todoDao: TodoDao
) : TodoRepository {override fun getAllTodos(): Flow<List<Todo>> = todoDao.getAll().map { list -> list.sortedByDescending { it.createdAt } }override suspend fun addTodo(todo: Todo) = todoDao.insert(todo)override suspend fun updateTodo(todo: Todo) = todoDao.update(todo)override suspend fun deleteTodo(todo: Todo) = todoDao.delete(todo)
}
4. ViewModel 实现
@HiltViewModel
class TodoViewModel @Inject constructor(private val repository: TodoRepository
) : ViewModel() {// UI 状态封装data class TodoUiState(val todos: List<Todo> = emptyList(),val isLoading: Boolean = false,val error: String? = null)// 使用 MutableStateFlow 管理状态private val _uiState = MutableStateFlow(TodoUiState(isLoading = true))val uiState: StateFlow<TodoUiState> = _uiState.asStateFlow()init {loadTodos()}private fun loadTodos() {viewModelScope.launch {repository.getAllTodos().catch { e -> _uiState.value = TodoUiState(error = e.message)}.collect { todos ->_uiState.value = TodoUiState(todos = todos)}}}fun addTodo(title: String, description: String = "") {viewModelScope.launch {val newTodo = Todo(title = title, description = description)repository.addTodo(newTodo)}}fun toggleTodoCompletion(todo: Todo) {viewModelScope.launch {repository.updateTodo(todo.copy(isCompleted = !todo.isCompleted))}}fun deleteTodo(todo: Todo) {viewModelScope.launch {repository.deleteTodo(todo)}}
}
5. Compose UI 实现
@Composable
fun TodoScreen(viewModel: TodoViewModel = viewModel()
) {val uiState by viewModel.uiState.collectAsState()var showDialog by remember { mutableStateOf(false) }var newTodoTitle by remember { mutableStateOf("") }Scaffold(topBar = { TopAppBar(title = { Text("Todo List") }) },floatingActionButton = {FloatingActionButton(onClick = { showDialog = true }) {Icon(Icons.Filled.Add, "Add Todo")}}) { padding ->when {uiState.isLoading -> Center { CircularProgressIndicator() }uiState.error != null -> Center { Text("Error: ${uiState.error}") }else -> TodoList(todos = uiState.todos,onToggleComplete = viewModel::toggleTodoCompletion,onDelete = viewModel::deleteTodo,modifier = Modifier.padding(padding))}}if (showDialog) {AlertDialog(onDismissRequest = { showDialog = false },title = { Text("Add New Todo") },text = {TextField(value = newTodoTitle,onValueChange = { newTodoTitle = it },label = { Text("Title") })},confirmButton = {Button(onClick = {viewModel.addTodo(newTodoTitle)newTodoTitle = ""showDialog = false},enabled = newTodoTitle.isNotBlank()) {Text("Add")}},dismissButton = {Button(onClick = { showDialog = false }) {Text("Cancel")}})}
}@Composable
fun TodoList(todos: List<Todo>,onToggleComplete: (Todo) -> Unit,onDelete: (Todo) -> Unit,modifier: Modifier = Modifier
) {LazyColumn(modifier = modifier) {items(items = todos,key = { it.id }) { todo ->TodoItem(todo = todo,onCheckedChange = { onToggleComplete(todo) },onDelete = { onDelete(todo) })}}
}@Composable
fun TodoItem(todo: Todo,onCheckedChange: (Boolean) -> Unit,onDelete: () -> Unit
) {Card(modifier = Modifier.fillMaxWidth().padding(8.dp),elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)) {Row(modifier = Modifier.padding(16.dp),verticalAlignment = Alignment.CenterVertically) {Checkbox(checked = todo.isCompleted,onCheckedChange = onCheckedChange)Column(modifier = Modifier.weight(1f)) {Text(text = todo.title,style = MaterialTheme.typography.titleMedium,textDecoration = if (todo.isCompleted) TextDecoration.LineThrough else null)if (todo.description.isNotBlank()) {Text(text = todo.description,style = MaterialTheme.typography.bodySmall)}}IconButton(onClick = onDelete) {Icon(Icons.Filled.Delete, "Delete")}}}
}
6. 数据库配置 (Room)
@Database(entities = [Todo::class], version = 1)
abstract class AppDatabase : RoomDatabase() {abstract fun todoDao(): TodoDao
}@Dao
interface TodoDao {@Query("SELECT * FROM todos")fun getAll(): Flow<List<Todo>>@Insertsuspend fun insert(todo: Todo)@Updatesuspend fun update(todo: Todo)@Deletesuspend fun delete(todo: Todo)
}
最佳实践要点
-
状态管理
- 使用密封类管理 UI 状态(Loading/Success/Error)
- ViewModel 暴露不可变
StateFlow
,Compose 通过collectAsState()
观察 - UI 组件保持无状态,通过参数接收数据
-
性能优化
- 使用
remember
缓存计算结果 - 为 LazyColumn 的
items
设置唯一 key - 使用
derivedStateOf
处理复杂状态转换
- 使用
-
架构分层
-
测试策略
- ViewModel 测试:使用
TestCoroutineDispatcher
- Compose UI 测试:使用
createComposeRule
- Repository 测试:Mock 数据源
- ViewModel 测试:使用
-
用户交互优化
- 添加 Undo 删除功能(使用 Snackbar)
- 实现本地搜索/过滤
- 添加拖拽排序支持
-
错误处理
- 在 Repository 捕获数据源异常
- ViewModel 将异常转换为用户友好消息
- UI 层显示错误状态并提供重试选项
此实现遵循了 Android 官方架构指南,结合了 Compose 的声明式特性和 MVVM 的响应式数据管理,提供了可测试、可维护的现代化 Android 应用架构。