Android 应用架构演进:MVC / MVP / MVVM / MVI (2026)

1 阅读16分钟

1. 核心结论

结论说明
官方架构指引Google 官方不指定某一种具体模式(MVC/MVP/MVVM),而是推荐分层架构 + 单向数据流(UDF),强调关注点分离和状态可预测。[1][2]
业界主流实践MVVM + 单一 UiState(吸收了 MVI 思想)是目前最常用的 UI 架构,能较好地解决复杂状态的同步问题。[20]
推荐技术栈UI:Compose;状态:StateFlow;依赖注入:Hilt(官方推荐,并非强制);数据:Repository。[2][8]
导航演进Navigation 3 把回退栈交给开发者以状态的形式管理,导航变成状态驱动,与 Compose 的声明式写法一致。[21]

2. 架构演进时间线

Android 应用架构大致经历了四个阶段(年份为大致区间,用于说明趋势,不是严格的起止点):

阶段大致时间代表模式特征遗留问题
早期2008 – 2013MVCMassive Activity,视图和逻辑严重耦合难以做单元测试,维护成本高
解耦期2013 – 2017MVP用 Contract 接口把视图和逻辑分开接口数量激增,生命周期管理仍然复杂
数据驱动期2017 – 2021MVVMViewModel + LiveData(2017 年 Architecture Components 发布),数据变化驱动视图更新多个数据源导致状态分散,一致性难以追踪
原则驱动期2021 – 至今MVI / UDF用单一 UiState 集中管理页面状态,单向数据流需要设计 UiState 和事件模型
MVC ──▶ MVP ──▶ MVVM ──▶ MVVM + 单一 UiState(MVI 风格)/ UDF
 耦合     接口解耦    数据驱动        状态集中、数据单向流动

3. MVC:原理与优缺点

Model-View-Controller

MVC 架构图

*图:MVC 架构(Activity/Fragment 同时充当 View 与 Controller)

核心痛点:在 Android 中,Activity 既负责 UI 初始化(View),又负责事件处理(Controller),两者边界模糊,最终变成 Massive View Controller。

优势

  • 结构简单直观:概念好理解,符合事件驱动编程的直觉。
  • 上手成本低:不需要额外的框架或抽象层。

劣势

  • Massive View Controller:业务逻辑和 UI 逻辑混在 Activity 里,代码量大,难以维护。
  • 难以单元测试:Controller 强依赖 Android SDK(Context、View),无法在纯 JVM 环境中测试。
  • 状态管理脆弱:屏幕旋转等配置变更会重建 Activity,不做特殊处理就会丢失 UI 状态。

适用场景:不建议用于现代 Android 生产项目,仅适合教学演示、极简原型验证(PoC)或一次性工具页面。


4. MVP:原理与特性分析

MVP 架构图

*图:MVP 架构(View 与 Presenter 通过 Contract 接口通信)

核心优势
通过 Contract 接口把 View 和 Presenter 分开。Presenter 不依赖 Android SDK,可以做纯 JVM 单元测试,测试效率和代码可靠性都明显提升。

主要痛点

  • 接口激增:每个页面都要定义 IView/IPresenter 接口,模板代码多。
  • 生命周期管理:Presenter 持有 View 引用。用 WeakReference 只能让 View 可以被回收,进行中的网络请求或协程仍要在 detachView() 时手动取消,否则容易内存泄漏或回调到已销毁的界面。[19][24]

配置变更处理
原生 MVP 不好处理屏幕旋转等配置变更。社区常用 Mosby 框架,借助 Fragment 的 setRetainInstance(true) 等机制保留 Presenter 实例。[19][24](setRetainInstance 在新版 Fragment 中已被废弃,这个方案主要见于老项目。)

示例(Kotlin)

interface UserContract {
    interface View { fun showLoading(); fun showUser(user: User); fun showError(msg: String) }
    interface Presenter { fun attach(view: View); fun detach(); fun loadUser(id: String) }
}

class UserPresenter(private val repo: UserRepository) : UserContract.Presenter {
    private var view: UserContract.View? = null
    private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)

    override fun attach(view: UserContract.View) { this.view = view }
    override fun detach() { view = null; scope.coroutineContext.cancelChildren() } // 必须手动取消

    override fun loadUser(id: String) {
        view?.showLoading()
        scope.launch {
            runCatching { repo.getUser(id) }
                .onSuccess { view?.showUser(it) }
                .onFailure { view?.showError(it.message ?: "error") }
        }
    }
}

适用场景:已有 MVP 架构、暂时不迁移的老项目。


5. MVVM:原理与分析

MVVM 架构图

*图:MVVM 架构(View 观察 ViewModel 暴露的状态)

核心原理

  • ViewModel 不持有 View:两者通过观察者模式通信。
  • 状态驱动 UI:UI 是状态的映射,数据变化后界面自动刷新。

优势

  • 配置变更后数据保留:屏幕旋转不丢数据。
  • 内存泄漏风险低:ViewModel 不引用 View,且感知生命周期(前提是不持有 View/Activity Context)。
  • 可测试性高:业务逻辑独立于 Android 框架。

挑战

  • 状态定义繁琐:需要仔细设计 UiState 数据类。
  • 一次性事件:Toast、导航等事件要谨慎处理。官方建议优先把事件建模成 UI 状态,由 UI 处理完后通知 ViewModel 清除;Channel 等方案属于社区做法,各有取舍。[4]

示例(Kotlin)

data class UserUiState(
    val isLoading: Boolean = false,
    val user: User? = null,
    val errorMessage: String? = null
)

@HiltViewModel
class UserViewModel @Inject constructor(
    private val repo: UserRepository
) : ViewModel() {
    private val _uiState = MutableStateFlow(UserUiState())
    val uiState: StateFlow<UserUiState> = _uiState.asStateFlow()

    fun loadUser(id: String) {
        _uiState.update { it.copy(isLoading = true) }
        viewModelScope.launch {
            runCatching { repo.getUser(id) }
                .onSuccess { u -> _uiState.update { it.copy(isLoading = false, user = u) } }
                .onFailure { e -> _uiState.update { it.copy(isLoading = false, errorMessage = e.message) } }
        }
    }
    fun errorShown() = _uiState.update { it.copy(errorMessage = null) }
}

适用场景:目前的主流选择,也是新项目的首选。官方的推荐模式(ViewModel 作为状态持有者 + UDF)与它最接近,结合 Jetpack 组件(StateFlow、Compose 等)效果最好。


6. 三种架构 9 维度对比

对比维度MVCMVPMVVM
1. 核心思想视图与模型分离,控制器调度面向接口编程,切断 V 与 M 的直接联系数据驱动视图,响应式编程
2. 职责划分Activity/Fragment 既是 V 也是 C,容易臃肿职责清晰,Presenter 处理纯逻辑职责清晰,ViewModel 专注状态管理
3. 引用关系View 依赖 Model,Controller 依赖两者View 与 Presenter 互相持有引用View 单向依赖 ViewModel
4. 可测试性差(强依赖 Android SDK)良好(可 Mock View 接口测试 P 层)很好(ViewModel 纯逻辑,无需 Mock View)
5. 耦合度高中(通过接口契约绑定)低(通过观察者模式松耦合)
6. 样板代码量少多(大量 View/Presenter 接口)中(UiState 类 + 观察代码)
7. 内存泄漏风险高(异步回调容易持有 Activity)较高(需手动解绑 View、取消任务)低(ViewModel 不持有 View,感知生命周期)
8. 配置变更支持差(旋转后数据丢失)差(需手动保存/恢复或借助 Mosby)好(ViewModel 实例自动保留)
9. 学习成本低中(需理解接口隔离)中高(需掌握协程/Flow 等响应式编程)

7. 按项目类型选型

项目类型推荐架构理由
新项目(Compose)MVVM + UDF与声明式 UI 的数据驱动特性契合。UDF 让状态可预测,ViewModel 负责管理 UI 状态,与官方推荐架构一致。
遗留 View 项目MVP 或渐进式 MVVM保持现有 MVP 稳定运行,避免大规模重构的风险;新功能模块逐步引入 MVVM,平滑过渡。
复杂交互 / 金融类MVI 风格的 MVVM状态变化频繁、对一致性要求严格时,Intent 驱动和单一状态源能减少状态冲突,提高健壮性。
超大型团队MVVM + 深度模块化结合 Clean Architecture 思想,按业务边界拆分模块;通过依赖注入和接口隔离,支持多团队并行开发。

8. 官方架构指南:分层结构

image.png 图:Android 官方推荐的应用分层架构(来源:developer.android.com [1])

层级职责主要组成
UI 层(UI Layer)在屏幕上显示应用数据,响应用户交互UI 元素(Activity / Compose)、状态持有者(ViewModel)
领域层(Domain Layer,可选)位于 UI 层和数据层之间,封装复杂业务逻辑,或被多个 ViewModel 复用的简单逻辑,避免重复 [5]UseCase / Interactor
数据层(Data Layer)包含应用数据和业务逻辑,遵循单一数据源(SSOT)原则,保证数据一致可靠 [6]Repository(SSOT)、数据源(网络 / 数据库)

9. 官方架构指南:单向数据流(UDF)

image.png

图:UI 层单向数据流(UDF)示意(来源:developer.android.com [3])

核心原则

  • 状态向下流动(State flows down)
  • 事件向上流动(Events flow up)

工作流程

  1. 数据层向 ViewModel 提供应用数据。
  2. ViewModel 处理数据,转换成 UI State 暴露给界面。
  3. UI 元素读取状态并渲染界面。
  4. 用户交互产生事件(Events),传回 ViewModel 处理,形成闭环。

说明:官方的“事件处理决策树”配图链接失效(404),本文用文字代替:UI 逻辑(如展开/折叠)在 UI 内处理;业务逻辑交给 ViewModel;ViewModel 产生的一次性事件优先建模为 UI 状态。[4]


10. 状态容器与 View 绑定对比

10.1 状态容器(State Holders)

方案定位特点适用场景
LiveData传统方案感知生命周期;setValue 只能在主线程调用,后台线程要用 postValue;操作符少简单的 UI 状态观察,Java 项目或遗留项目
StateFlow官方推荐必须有初始值,天然符合 UI 状态的连续性;基于协程,可切换 Dispatcher;自带去重(相同值不重复发射)暴露 UI 状态(UiState)
SharedFlow事件流没有初始值;replay 和 buffer 策略可配置多订阅者的事件广播;官方不建议用它发送一次性 UI 事件(如 Toast)

在 Compose 或 Lifecycle 中收集 StateFlow 时,建议使用 collectAsStateWithLifecycle() / repeatOnLifecycle,避免界面在后台时继续收集。

10.2 视图绑定

方案特点建议
ViewBinding(首选)不需要注解处理,构建开销小;自动生成绑定类,类型安全、空安全,避免 findViewById 导致的 NPE基于 View 的项目首选
DataBinding支持双向绑定,可以在 XML 中写表达式;需要注解处理,大型项目构建较慢;逻辑混在 XML 里不好排查新项目一般不推荐;Compose 项目不需要

11. Compose 状态提升与 Navigation 3

11.1 状态提升(State Hoisting)[7]

把状态移到使用它的各个可组合项的最低公共祖先,遵循 UDF:状态以参数形式向下传,事件以 Lambda 形式向上传。

Compose 状态提升示意图

图:状态向下、事件向上的状态提升示意,来源:本 PPT 第 13 页

状态持有者管理内容
Composable 本身简单的 UI 元素状态(如展开/折叠)
普通 State Holder 类复杂的 UI 逻辑和状态
ViewModel业务逻辑和屏幕级状态,生命周期更长
@Composable
fun UserRoute(viewModel: UserViewModel = hiltViewModel()) {
    val uiState by viewModel.uiState.collectAsStateWithLifecycle()
    UserScreen(uiState = uiState, onRetry = { viewModel.loadUser("42") })
}

@Composable
fun UserScreen(uiState: UserUiState, onRetry: () -> Unit) { /* 无状态、易预览、易测试 */ }

11.2 Navigation 3:状态驱动导航 [21]

Navigation 3 用 Compose 的响应式写法取代命令式导航:

  • 回退栈由开发者持有:回退栈是一个可观察的状态列表,导航就是对这个列表的增删,UI 根据它自动渲染。
  • 类型安全:路由是 Kotlin 类型(可配合 Kotlin Serialization),不再拼接字符串路由。
// 示意代码:表达“导航即状态变更”的思路,具体 API 以官方文档为准
@Serializable data object Home : NavKey
@Serializable data class Profile(val id: String) : NavKey

val backStack = rememberNavBackStack(Home)

NavDisplay(
    backStack = backStack,
    onBack = { backStack.removeLastOrNull() },
    entryProvider = entryProvider {
        entry<Home> { HomeScreen(onOpen = { id -> backStack.add(Profile(id)) }) }
        entry<Profile> { key -> ProfileScreen(key.id) }
    }
)

12. MVI 与 MVVM 融合

MVI 的核心理念

  • 单一状态对象:UI 状态放在一个不可变数据类里,避免多个状态流之间不一致。
  • 显式 Intent:用户操作抽象成明确的意图(Intent/Action),统一交给 ViewModel。
  • 纯函数 Reducer:根据当前状态和新事件,用纯函数算出新状态,结果可预测。

现代 MVVM 吸收了哪些做法

用单一 UiState 聚合所有状态,取代多个 LiveData:

data class NewsUiState(
    val isLoading: Boolean = false,
    val articles: List<Article> = emptyList()
)

用 MutableStateFlow.update 原子地更新状态,作用类似 Reducer:

// 线程安全的原子更新
_uiState.update { currentState ->
    currentState.copy(isLoading = true)
}

注意:“UDF 本质是 MVI 变体”主要是社区观点 [20],不是官方定义。比较稳妥的说法是:官方推荐架构(UDF + StateFlow)是吸收了 MVI 优点的改良版 MVVM。


13. Clean Architecture 与模块化

13.1 Clean Architecture 核心 [12]

  • 依赖规则:源码依赖只能向内,指向更高层的策略。
  • 外层(UI、DB、Network)依赖内层(Entities、UseCases),内层不知道外层的存在。
  • 业务逻辑独立于框架、UI 和数据库,便于测试。

与官方分层的关系:官方架构中领域层是可选的,依赖方向为 UI → Domain → Data。这比经典 Clean Architecture 更轻量、更务实。[1][5]

13.2 UseCase 的争议与实践

  • 争议点:如果 UseCase 只是简单转发(ViewModel → UseCase → Repository),就成了多余的样板代码。
  • 实践建议(参考 Now in Android [9]):
    • 用 UseCase 封装复杂业务逻辑;
    • 用来组合多个 Repository;
    • 避免纯转发;
    • 按需引入:简单 CRUD 直接调用 Repository,复杂交互再引入 UseCase。

13.3 现代 Android 模块化分层

模块化可以支持多团队并行开发、提升编译速度,并强制隔离模块边界。

模块化分层图

图:现代 Android 模块化分层(:app → :feature → :core),来源:本 PPT 第 15 页


14. 应用案例(一):官方示例

项目定位亮点
architecture-samples [10]官方架构蓝图示例main 分支:Compose + Hilt + Flow;views 分支:XML + Navigation
Now in Android(NiA) [9]现代 Android 开发示范项目官方架构学习路径文档中的分步响应式数据流讲解;离线优先(Offline-first)架构
Sunflower [11]园艺应用示例从 View 迁移到 Compose 的完整过程,混合 UI 架构的示范(仓库已归档,仍可作参考)

15. 应用案例(二):行业实践

15.1 国际企业

企业 / 框架架构特点
Airbnb Mavericks / Trio [16][17]Mavericks 是 Airbnb 开源的 MVI 风格框架;Trio 在此基础上面向 Compose,把导航也当作状态的一部分,统一管理 UI 状态和路由状态,简化复杂页面的状态同步。
Square Workflow [18][25]采用状态机树模型,把应用逻辑抽象成可组合的工作流,严格控制状态流转。Square 在 POS(收银)等业务中用它满足对状态确定性和可测试性的高要求。
Uber RIBs [22]Router、Interactor、Builder 架构。由业务逻辑驱动而不是视图驱动,用树形结构实现业务隔离,支撑大型团队协作开发。
Slack Circuit [23]面向 Compose 和 Coroutines 的架构框架,提供简洁的 Presenter/UI 契约,代表了声明式 UI 时代的架构方向。

15.2 国内大厂

美团技术团队

  • 2016 年,MVVM 早期探索 [13]:较早在国内引入 DataBinding 和 MVVM,解决复杂表单、列表的视图更新问题,提高了可维护性。
  • 2018 年起,平台化架构 [14][15]:业务膨胀后,美团外卖演进为以组件化、平台化为核心的架构(如 WMRouter 支撑多 App 复用),强调底层能力复用和工程效率。

字节跳动 / 阿里巴巴(⚠️ 基于搜索摘要整理,未核对原文)

  • 整体趋势是向 MVI / UDF 范式靠拢:规范数据流向,减少复杂交互下的状态竞争;
  • 把 UI 看作某一时刻状态的不可变快照,配合 Diff 优化渲染;
  • 架构思想与前端(React/Vue)和跨端框架(Flutter)逐渐趋同。

16. 最佳实践:选型决策与迁移

16.1 新项目架构选型

层次推荐说明
UIJetpack Compose声明式 UI
VMMVVM(UDF)单向数据流的状态管理
DIHilt官方推荐的依赖注入方案(不是强制要求)
DataRepository单一数据源原则

16.2 从 MVP 迁移到 MVVM 的 5 个步骤

  1. 引入 ViewModel:让 Presenter 持有 ViewModel,或直接把 Presenter 改成 ViewModel,交给它处理生命周期。
  2. 定义 UiState:把分散的 View 接口回调(如 showLoading、showData)合并成一个不可变的状态数据类。
  3. 改变状态暴露方式:用 StateFlow(或 LiveData)暴露 UiState,View 层改为订阅状态。
  4. 逐步移除 Contract:View 接口被状态订阅取代、Presenter 接口被 ViewModel 方法取代后,删除 Contract 类。
  5. 引入 Repository:把 ViewModel 中获取数据的逻辑移到 Repository,统一管理数据源。

建议按页面或功能模块逐个迁移,每迁移一个就补齐 ViewModel 单元测试,作为回归保障。


17. 最佳实践:技术栈、测试与反模式

17.1 推荐技术栈

领域推荐
UI 层Jetpack Compose / ViewBinding
状态管理与异步StateFlow + Coroutines
依赖注入Hilt(基于 Dagger)
数据层Repository + Room + Retrofit
导航Navigation 3(类型安全)

17.2 测试策略

测试对象方法
ViewModel重点断言状态:输入事件后,检查 StateFlow 暴露的状态是否符合预期;可用 Turbine 测试 Flow 的发射序列。
Repository用 Fake 实现(官方更推荐)或 MockK 隔离网络、数据库等外部依赖,验证业务逻辑和数据转换。
UI用 Compose 语义(Semantics)测试检查 UI 节点的存在、状态和交互;基于 View 的项目继续使用 Espresso。

17.3 常见反模式

反模式问题正确做法
ViewModel 持有 View / Activity ContextViewModel 比 Activity/Fragment 活得久,会导致内存泄漏只暴露状态;确实需要 Context 时用 Application Context
UI 层直接访问数据源Activity 直接调用 Room/Retrofit,破坏分层和单向数据流经由 ViewModel → Repository
用 SharedFlow 发送一次性事件配置变更或界面不在前台时,事件可能丢失或被重复处理把事件建模为 UI 状态的一部分 [4]
滥用 GlobalScope无法控制协程生命周期,后台任务泄漏使用 viewModelScope / lifecycleScope
多个分散的 LiveData/StateFlow 暴露同一页面状态状态之间不一致,难以追踪合并为单一 UiState
UseCase 只做转发增加样板代码,没有实际价值只在逻辑复杂或需要复用时引入

18. 总结与展望

  • 状态管理思想的进化:架构演进的本质不只是代码结构或分层的变化,而是对“状态”的认识和管理方式在升级:从混乱到有序,从命令式到声明式。
  • 清晰管理状态和数据流:架构的价值不在于叫 MVC、MVP、MVVM 还是 MVI,而在于复杂业务中能否提供清晰、可预测的数据流转。
  • UDF 与 SSOT:无论采用哪种模式,坚持单向数据流(UDF)和单一数据源(SSOT),都是降低系统复杂度、避免状态不一致的基础。

最终结论:守住核心原则,不盲目追随模式标签,才能保证 Android 应用架构长期可维护、可扩展。


19. 参考资料

参考资料

  1. developer.android.com - Guide to app architecture
  2. developer.android.com - Recommendations for Android architecture
  3. developer.android.com - UI layer
  4. developer.android.com - UI layer events
  5. developer.android.com - Domain layer
  6. developer.android.com - Data layer
  7. developer.android.com - State hoisting in Compose
  8. developer.android.com - Dependency injection with Hilt
  9. github.com - android/nowinandroi…
  10. github.com - android/architectur…
  11. github.com - android/sunflower
  12. blog.cleancoder.com - The Clean Architecture (2012-08-13)
  13. tech.meituan.com - 如何构建Android MVVM应用框架 (2016-11-11)
  14. tech.meituan.com - 美团外卖Android平台化架构演进 (2018-03-16)
  15. tech.meituan.com - WMRouter:美团外卖Android多App复用实践 (2018-09-27)
  16. medium.com - Airbnb Tech - Introducing Trio: Part I (2024-03)
  17. github.com - airbnb/mavericks
  18. square.github.io - Workflow Concepts
  19. hannesdorfmann.com - Mosby: A Model-View-Presenter library
  20. proandroiddev.com - Google's UDF vs real world MVI-flavored MVVM
  21. infoq.com - Android Jetpack Navigation 3 Overview (2025-11)
  22. github.com - Uber RIBs
  23. slackhq.github.io - Slack Circuit
  24. proandroiddev.com - Get your MVP right with Mosby (2017-05-15)
  25. developer.squareup.com - Why Workflow: Scaling Square Point of Sale