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

1. 核心结论

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

2. 架构演进时间线

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

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

3. MVC:原理与优缺点

Model-View-Controller

*图: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 架构(View 与 Presenter 通过 Contract 接口通信)

核心优势

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

主要痛点

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

配置变更处理

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

示例(Kotlin)

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 架构(View 观察 ViewModel 暴露的状态)

核心原理

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

优势

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

挑战

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

示例(Kotlin)

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 维度对比

对比维度 MVC MVP MVVM
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. 官方架构指南:分层结构

图: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)

图: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.1 状态提升(State Hoisting)7

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

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

状态持有者 管理内容
Composable 本身 简单的 UI 元素状态(如展开/折叠)
普通 State Holder 类 复杂的 UI 逻辑和状态
ViewModel 业务逻辑和屏幕级状态,生命周期更长
kotlin 复制代码
@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) { /* 无状态、易预览、易测试 */ }

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

  • 回退栈由开发者持有:回退栈是一个可观察的状态列表,导航就是对这个列表的增删,UI 根据它自动渲染。
  • 类型安全:路由是 Kotlin 类型(可配合 Kotlin Serialization),不再拼接字符串路由。
kotlin 复制代码
// 示意代码:表达"导航即状态变更"的思路,具体 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:

kotlin 复制代码
data class NewsUiState(
    val isLoading: Boolean = false,
    val articles: List<Article> = emptyList()
)

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

kotlin 复制代码
// 线程安全的原子更新
_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 更轻量、更务实。15

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

15.2 国内大厂

美团技术团队

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

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

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

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

16.1 新项目架构选型

层次 推荐 说明
UI Jetpack Compose 声明式 UI
VM MVVM(UDF) 单向数据流的状态管理
DI Hilt 官方推荐的依赖注入方案(不是强制要求)
Data Repository 单一数据源原则

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 Context ViewModel 比 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
相关推荐
木头左4 小时前
【架构设计】教育类 App 服务端化改造:AI 上移、题库分层与个人题库归属
android·架构设计·系统设计·后端开发·数据库设计
墨狂之逸才5 小时前
USB 的 HOST 和 DEVICE 到底有什么区别?ADB 调试应该选哪个?
android
奶油话梅糖6 小时前
苹果的内核跑上了安卓手机:2,631 Star 的 HakiOS 让 XNU 在 Adreno GPU 上画出 118 帧
android·macos·智能手机
恋猫de小郭6 小时前
Flutter THLC 的 WebView 滚动终于从 70 FPS 提升到 120 FPS
android·前端·flutter
好的佩奇17 小时前
Dart 之扩展类型
android·flutter·dart
alexhilton19 小时前
2027 Android 开发者路线图:学习、面试与 AI
android·kotlin·android jetpack
灿飞屋21 小时前
Kotlin与Java虚拟机三:控制流
android·java·开发语言·后端·python·kotlin·安卓
疯华整猫1 天前
我用 Trae 把 Android 内存账重算了一遍
android·性能优化·源码
TimeFine1 天前
AI 编程工作流的设计:从人工判断到 AI裁判
android·ai编程