新!最全 MVI + Flow Android行为驱动设计--(介绍篇)

474 阅读2分钟

前言

在 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

微信图片_20250130001053.png

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组件利用组件树进行搭建。