[Android 从零到一] 架构演进:从 MVC 到 MVP、MVVM 与 MVI 的落地思路

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

为什么需要架构

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

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

MVC:最早的分层思路

基本结构

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

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

    Android 中的 MVC

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

    
    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 接口引用,处理业务逻辑
  • 
    用户操作 → View 接口 → Presenter → 更新 Model
                              ↓
                       通过 View 接口刷新 UI
    

    实战代码

    首先定义 View 接口:

    
    interface UserView {
        fun showLoading()
        fun hideLoading()
        fun showUsers(users: List)
        fun showError(message: String)
    }
    

    Presenter 持有 View 接口引用:

    
    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 接口:

    
    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
  • 
    用户操作 → View → ViewModel → Repository
                    ↑
          LiveData / StateFlow 自动推送新数据
    

    实战代码

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

    
    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 观察状态变化:

    
    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
  • 
    用户操作 → Intent → Model 处理 → 新 State → View 渲染
    

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

    实战代码

    定义 Intent 和 State:

    
    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:

    
    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:

    
    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 这些组件本身就是为架构设计的。充分利用它们可以减少自建轮子的成本,也能让团队更快达成共识。

    posted @ 2026-07-12 10:16  天总会晴的  阅读(22)  评论(0)    收藏  举报