前言
MVI(Model-View-Intent)架构是近年来 Android 开发中逐渐流行的一种架构模式。它旨在解决 MVVM 在逻辑复杂时需要维护多个 LiveData(可变与不可变混合)的问题。MVI 通过 ViewState 对应用状态进行集中管理,页面只需订阅一个 LiveData 即可获取所有状态信息。
通过集中管理 ViewState,MVI 架构对外只暴露一个 LiveData,有效解决了 MVVM 模式下 LiveData 膨胀的问题,使得数据流向更加清晰单一。
然而,将所有页面状态都通过一个 LiveData 来管理也带来了一个显著的性能问题,即页面不支持局部刷新。当状态中的某个非关键属性发生变化时,整个 LiveData 都会触发更新,导致 View 层重新渲染所有内容。虽然对于 RecyclerView 可以通过 DiffUtil 来解决部分列表项的更新,但并非所有页面都是基于 RecyclerView 构建的,且引入 DiffUtil 会增加一定的开发成本。因此,直接使用基础 MVI 架构可能会带来不必要的性能损耗,这也是许多开发者犹豫是否采用 MVI 架构的原因之一。
本文主要介绍如何通过监听 LiveData 的属性变化,来实现 MVI 架构下的局部刷新,从而在保持架构优势的同时优化性能。
Mavericks 框架介绍
Mavericks 是 Airbnb 开源的一个 MVI 框架。该框架基于 Android Jetpack 与 Kotlin Coroutines,主要目标是使页面开发更高效、更容易、更有趣。目前,Mavericks 已经在 Airbnb 的数百个页面上投入使用。
下面我们来查看一下 Mavericks 的基本使用方式:
// 1. 包含页面所有状态的 data class
// 必须继承 MavericksState 接口
data class CounterState(val count: Int = 0) : MavericksState
// 2. 负责处理业务逻辑的 ViewModel
// 继承 MavericksViewModel,易于单元测试
class CounterViewModel(initialState: CounterState) : MavericksViewModel<CounterState>(initialState) {
// 通过 setState 更新页面状态
fun incrementCount() = setState { copy(count = count + 1) }
}
// 3. View 层,必须实现 MavericksView 接口
class CounterFragment : Fragment(R.layout.counter_fragment), MavericksView {
private val viewModel: CounterViewModel by fragmentViewModel()
{
counterText.setOnClickListener {
viewModel.incrementCount()
}
}
= withState(viewModel) { state ->
counterText.text =
}
}

