首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >[Android 从零到一] 架构演进:从 MVC 到 MVP、MVVM 与 MVI

[Android 从零到一] 架构演进:从 MVC 到 MVP、MVVM 与 MVI

原创
作者头像
hunter android
发布2026-07-13 10:30:44
发布2026-07-13 10:30:44
2170
举报

架构演进:从 MVC 到 MVP、MVVM 与 MVI 的落地思路

为什么需要架构

一个 Android 项目刚开始写的时候,代码量小,逻辑集中在 Activity 或 Fragment 里也还能接受。但随着功能增长,界面复杂,网络请求、数据缓存、页面状态、业务判断全部堆在一起,修改一个功能往往牵连一片代码,测试也变得困难。

架构的核心目标不是让代码"看起来更高级",而是让代码更容易维护、更容易测试、更容易协作。它解决的是"代码放在哪里"和"模块之间怎么通信"这两个问题。

MVC:最早的分层思路

基本结构

MVC(Model-View-Controller)把应用分为三层:

  • **Model**:数据和业务逻辑
  • **View**:界面展示
  • **Controller**:接收用户输入,协调 Model 和 View
代码语言:javascript
复制
用户操作 → Controller → 更新 Model → 通知 View 刷新

Android 中的 MVC

Android 框架本身带有 MVC 的影子。Activity/Fragment 承担了 Controller 和 View 的双重职责:

代码语言:javascript
复制
class UserActivity : AppCompatActivity() {

    // 相当于 Controller
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_user)

        // 发起请求
        UserRepository.getUsers { users ->
            // 直接操作 View
            val adapter = UserAdapter(users)
            recyclerView.adapter = adapter
        }
    }
}

MVC 的问题

在 Android 实际开发中,Activity 同时扮演了 Controller 和 View,导致:

  • Activity 代码膨胀,动辄上千行
  • 业务逻辑和 UI 操作耦合,难以单独测试
  • Model 变化时需要直接操作 View,依赖关系不清晰

这不是经典 MVC 的错,而是 Android 的组件模型让 Controller 和 View 很难彻底分离。

MVP:把逻辑从 Activity 中抽出来

基本结构

MVP(Model-View-Presenter)引入了 Presenter 层:

  • **Model**:数据和业务逻辑
  • **View**:只做界面展示,通过接口与 Presenter 交互
  • **Presenter**:持有 View 接口引用,处理业务逻辑
代码语言:javascript
复制
用户操作 → View 接口 → Presenter → 更新 Model
                          ↓
                   通过 View 接口刷新 UI

实战代码

首先定义 View 接口:

代码语言:javascript
复制
interface UserView {
    fun showLoading()
    fun hideLoading()
    fun showUsers(users: List)
    fun showError(message: String)
}

Presenter 持有 View 接口引用:

代码语言:javascript
复制
class UserPresenter(private val view: UserView) {

    private val repository = UserRepository()

    fun loadUsers() {
        view.showLoading()
        repository.getUsers(
            onSuccess = { users ->
                view.hideLoading()
                view.showUsers(users)
            },
            onError = { error ->
                view.hideLoading()
                view.showError(error.message ?: "加载失败")
            }
        )
    }
}

Activity 实现 View 接口:

代码语言:javascript
复制
class UserActivity : AppCompatActivity(), UserView {

    private val presenter = UserPresenter(this)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_user)
        presenter.loadUsers()
    }

    override fun showLoading() { progressBar.visibility = View.VISIBLE }
    override fun hideLoading() { progressBar.visibility = View.GONE }

    override fun showUsers(users: List) {
        recyclerView.adapter = UserAdapter(users)
    }

    override fun showError(message: String) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
    }
}

MVP 的优势与不足

**优势:**

  • 业务逻辑从 Activity 中剥离到 Presenter,职责清晰
  • View 通过接口交互,Presenter 可以脱离 Android 框架做单元测试
  • 一个 Presenter 可以对接多个不同的 View 实现

**不足:**

  • 每个功能都需要定义 View 接口、Presenter、Model,类和接口数量膨胀
  • Presenter 直接持有 View 引用,生命周期管理容易出内存泄漏
  • 页面复杂时 Presenter 本身也会膨胀

MVVM:数据驱动 + 生命周期感知

基本结构

MVVM(Model-View-ViewModel)借助 Jetpack 组件实现响应式数据绑定:

  • **Model**:数据和业务逻辑(Repository + 数据源)
  • **View**:Activity / Fragment / Compose,观察 ViewModel 中的数据变化
  • **ViewModel**:持有 UI 状态,通过 LiveData / StateFlow 通知 View
代码语言:javascript
复制
用户操作 → View → ViewModel → Repository
                ↑
      LiveData / StateFlow 自动推送新数据

实战代码

ViewModel 负责维护状态和调用 Repository:

代码语言:javascript
复制
class UserViewModel(
    private val repository: UserRepository
) : ViewModel() {

    private val _uiState = MutableStateFlow(UserUiState.Loading)
    val uiState: StateFlow = _uiState

    fun loadUsers() {
        viewModelScope.launch {
            _uiState.value = UserUiState.Loading
            try {
                val users = repository.getUsers()
                _uiState.value = UserUiState.Success(users)
            } catch (e: Exception) {
                _uiState.value = UserUiState.Error(e.message ?: "加载失败")
            }
        }
    }
}

sealed class UserUiState {
    data object Loading : UserUiState()
    data class Success(val users: List) : UserUiState()
    data class Error(val message: String) : UserUiState()
}

View 观察状态变化:

代码语言:javascript
复制
class UserActivity : AppCompatActivity() {

    private val viewModel: UserViewModel by viewModels {
        UserViewModelFactory(UserRepository())
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_user)

        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect { state ->
                    when (state) {
                        is UserUiState.Loading -> {
                            progressBar.visibility = View.VISIBLE
                        }
                        is UserUiState.Success -> {
                            progressBar.visibility = View.GONE
                            recyclerView.adapter = UserAdapter(state.users)
                        }
                        is UserUiState.Error -> {
                            progressBar.visibility = View.GONE
                            Toast.makeText(this@UserActivity, state.message, Toast.LENGTH_SHORT).show()
                        }
                    }
                }
            }
        }

        viewModel.loadUsers()
    }
}

MVVM 的优势

  • ViewModel 由 Jetpack 管理,自动感知生命周期,不会因配置变更丢失数据
  • StateFlow / LiveData 让数据推送自动绑定到 View,不需要手动持有 View 引用
  • sealed class 定义 UI 状态,类型安全,状态覆盖完整
  • 天然支持单向数据流:View 只读状态,ViewModel 只写状态

MVVM 的边界

  • ViewModel 里塞太多逻辑仍然会膨胀,需要配合 UseCase 或 Clean Architecture 拆分
  • 多个 StateFlow 组合时,状态同步容易出问题
  • View 层如果直接调用 ViewModel 中的多个方法,状态变化顺序难以保证

MVI:严格的单向数据流

基本结构

MVI(Model-View-Intent)把交互进一步规范化:

  • **Intent**:用户意图,所有用户操作都封装成 Intent
  • **Model**:处理 Intent,生成新的 State
  • **View**:根据 State 渲染 UI
代码语言:javascript
复制
用户操作 → Intent → Model 处理 → 新 State → View 渲染

核心约束是:View 只能发出 Intent,Model 只能产出 State,数据单向流动。

实战代码

定义 Intent 和 State:

代码语言:javascript
复制
sealed class UserIntent {
    data object LoadUsers : UserIntent()
    data class RefreshUser(val userId: String) : UserIntent()
}

data class UserState(
    val isLoading: Boolean = false,
    val users: List = emptyList(),
    val error: String? = null
)

ViewModel 统一处理 Intent:

代码语言:javascript
复制
class UserViewModel(
    private val repository: UserRepository
) : ViewModel() {

    private val _state = MutableStateFlow(UserState())
    val state: StateFlow = _state

    fun dispatch(intent: UserIntent) {
        when (intent) {
            is UserIntent.LoadUsers -> loadUsers()
            is UserIntent.RefreshUser -> refreshUser(intent.userId)
        }
    }

    private fun loadUsers() {
        viewModelScope.launch {
            _state.update { it.copy(isLoading = true, error = null) }
            try {
                val users = repository.getUsers()
                _state.update { it.copy(isLoading = false, users = users) }
            } catch (e: Exception) {
                _state.update { it.copy(isLoading = false, error = e.message) }
            }
        }
    }

    private fun refreshUser(userId: String) {
        viewModelScope.launch {
            try {
                val refreshed = repository.refreshUser(userId)
                _state.update { state ->
                    state.copy(users = state.users.map {
                        if (it.id == userId) refreshed else it
                    })
                }
            } catch (e: Exception) {
                _state.update { it.copy(error = e.message) }
            }
        }
    }
}

View 只发送 Intent,只观察 State:

代码语言:javascript
复制
class UserActivity : AppCompatActivity() {

    private val viewModel: UserViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_user)

        // 发起加载意图
        viewModel.dispatch(UserIntent.LoadUsers)

        // 刷新按钮点击 → 发送意图
        refreshButton.setOnClickListener {
            viewModel.dispatch(UserIntent.RefreshUser(currentUserId))
        }

        // 观察状态
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.state.collect { state ->
                    progressBar.visibility = if (state.isLoading) View.VISIBLE else View.GONE
                    if (state.users.isNotEmpty()) {
                        recyclerView.adapter = UserAdapter(state.users)
                    }
                    state.error?.let { Toast.makeText(this@UserActivity, it, Toast.LENGTH_SHORT).show() }
                }
            }
        }
    }
}

MVI 的优势

  • 所有用户操作都通过 Intent 发出,行为可追踪、可回放
  • State 是完整的页面快照,任何时刻的状态都是确定的
  • 减少中间状态不一致的问题,适合复杂交互页面

MVI 的代价

  • 简单页面使用 MVI 显得过度设计
  • 每次状态变化都产生完整 State 对象,频繁更新时需要注意性能
  • Intent 和 State 的类定义数量较多

四种架构对比

| 维度 | MVC | MVP | MVVM | MVI |

|------|-----|-----|------|-----|

| 关注点分离 | 一般 | 好 | 好 | 好 |

| 可测试性 | 差 | 好 | 好 | 好 |

| 代码量 | 少 | 多 | 中等 | 多 |

| 数据流方向 | 不明确 | 双向 | 单向(推荐) | 严格单向 |

| 生命周期处理 | 手动 | 手动 | 自动 | 自动 |

| 适用场景 | 简单项目 | 中等项目 | 大多数项目 | 复杂交互页面 |

如何选择

没有一种架构适合所有场景。实际项目中可以混合使用:

  • **简单页面**(设置页、关于页):MVVM 足够,不需要复杂的 Intent 体系
  • **列表 + 详情页面**:MVVM + Repository 模式,数据流清晰
  • **复杂交互页面**(多 Tab、表单、实时状态):MVI 的严格单向数据流能减少状态同步问题
  • **老旧项目改造**:先引入 MVP 将逻辑从 Activity 中剥离,再逐步迁移到 MVVM

架构选型的关键不在于"哪种更先进",而在于"哪种能解决当前的问题"。团队规模、项目阶段、维护成本都是需要考虑的因素。

落地建议

不要一步到位

如果一个项目还在用 MVC 甚至没有架构,直接跳到 MVI 会引发大量重构。可以分步进行:先把网络请求和数据操作收到 Repository,再引入 ViewModel 管理状态,最后在复杂页面引入 Intent 机制。

保持一致性

同一个项目或同一个模块内,架构风格要统一。如果一部分用 MVVM、一部分用 MVI、一部分直接写在 Activity 里,维护成本会比不用架构更高。

分层不是目的

不要为了分层而分层。如果一个功能很简单,三层代码都是空壳转发,那不如简化。架构是手段,不是目的。

善用 Jetpack 组件

ViewModel、LiveData、StateFlow、Navigation、Room 这些组件本身就是为架构设计的。充分利用它们可以减少自建轮子的成本,也能让团队更快达成共识。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 架构演进:从 MVC 到 MVP、MVVM 与 MVI 的落地思路
    • 为什么需要架构
    • MVC:最早的分层思路
      • 基本结构
      • Android 中的 MVC
      • MVC 的问题
    • MVP:把逻辑从 Activity 中抽出来
      • 基本结构
      • 实战代码
      • MVP 的优势与不足
    • MVVM:数据驱动 + 生命周期感知
      • 基本结构
      • 实战代码
      • MVVM 的优势
      • MVVM 的边界
    • MVI:严格的单向数据流
      • 基本结构
      • 实战代码
      • MVI 的优势
      • MVI 的代价
    • 四种架构对比
    • 如何选择
    • 落地建议
      • 不要一步到位
      • 保持一致性
      • 分层不是目的
      • 善用 Jetpack 组件
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档