前言
在 Android 现代开发中,MVI(Model-View-Intent)架构结合 Kotlin Flow 已成为处理 UI 交互的一种强大方式。本文将深入探讨如何使用 MVI + Flow 实现行为驱动(Intent-Driven)的 UI 设计模式。
章节一
为什么选择 MVI + Flow?
1. 单向数据流
MVI 强调数据流的单向性,使 UI 状态的管理更清晰,避免双向数据绑定可能导致的复杂性。
2. 响应式编程
结合 Kotlin Flow,使 UI 层能够基于数据流进行响应式更新,避免回调地狱,提高可读性。
3. 更易测试
将 UI 逻辑与状态管理解耦,使 ViewModel 变得更容易单元测试。
MVI 核心组成
MVI 由三大核心组件组成:
1. View(视图层)
- 负责渲染 UI,并订阅 ViewModel 传来的状态。
- 监听用户输入,并将其转换为
Intent(意图)。
2. Intent(意图层)
- 代表用户的操作,如点击按钮、滑动列表等。
- 由 View 层触发,传递给 ViewModel 进行处理。
3. Model(数据层)
- 由 ViewModel 维护的 UI 状态,包含业务逻辑。
- 通过 Kotlin Flow 让 View 订阅数据流。
代码实现:MVI + Flow
1. 定义 UI 状态
sealed class UiState {
object Loading : UiState()
data class Success(val data: List<String>) : UiState()
data class Error(val message: String) : UiState()
}
2. 定义用户意图(Intent)
sealed class UserIntent {
object LoadData : UserIntent()
data class ClickItem(val id: Int) : UserIntent()
}
3. ViewModel 处理 Intent 并更新 UI 状态
class MainViewModel : ViewModel() {
private val _state = MutableStateFlow<UiState>(UiState.Loading)
val state: StateFlow<UiState> = _state.asStateFlow()
private val _intent = MutableSharedFlow<UserIntent>()
init {
handleIntent()
}
fun sendIntent(intent: UserIntent) {
viewModelScope.launch { _intent.emit(intent) }
}
private fun handleIntent() {
viewModelScope.launch {
_intent.collect { intent ->
when (intent) {
is UserIntent.LoadData -> loadData()
is UserIntent.ClickItem -> handleItemClick(intent.id)
}
}
}
}
// 模拟网络请求
private fun loadData() {
viewModelScope.launch {
_state.value = UiState.Loading
delay(1000)
_state.value = UiState.Success(listOf("Item1", "Item2", "Item3"))
}
}
private fun handleItemClick(id: Int) {
println("Clicked item with ID: $id")
}
}
4. 在 Compose 中绑定 ViewModel
会使用Hilt注入能力的同学,可以使用hiltViewModel进行注入 使用 Hilt 实现依赖项注入 | Android Developers
hilt-android-gradle-plugin = { module = "com.google.dagger:hilt-android-gradle-plugin", version.ref = "hiltAndroidGradlePlugin" }
hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "hiltAndroidGradlePlugin" }
@Composable
fun MainScreen(viewModel: MainViewModel = viewModel()) {
val uiState by viewModel.state.collectAsState()
LaunchedEffect(Unit) {
viewModel.sendIntent(UserIntent.LoadData)
}
when (uiState) {
is UiState.Loading -> CircularProgressIndicator()
is UiState.Success -> {
val data = (uiState as UiState.Success).data
LazyColumn {
items(data) { item ->
Text(text = item, modifier = Modifier
.fillMaxWidth()
.clickable { viewModel.sendIntent(UserIntent.ClickItem(data.indexOf(item))) })
}
}
}
is UiState.Error -> Text("Error: ${(uiState as UiState.Error).message}")
}
}
总结
- MVI 通过
Intent -> ViewModel 处理 -> State 更新 -> View 渲染的方式,确保数据流的单向性。 - Kotlin Flow 让状态管理更具响应式特性,避免回调嵌套。
- 结合 Jetpack Compose,实现高效、清晰的 UI 交互。
MVI + Flow 是现代 Android 开发中的趋势,能够提升代码质量和可维护性。如果你还没有尝试过,不妨在你的项目中实践一下!下一章节,将详细介绍如何用此框架驱动Compose组件利用组件树进行搭建。