一、从 MVC 到 MVVM:架构演进之路

做 Android 开发这些年,我亲眼见证了项目架构的几次大变革。说实话,早期写代码那会儿,哪有什么架构概念?一个 Activity 动辄上千行,业务逻辑和 UI 搅在一起,改个需求能让人崩溃。

后来慢慢接触了 MVC、MVP,再到现在的 MVVM,每一步演进其实都是在解决一个核心问题:代码该怎么组织,才能让项目活得更久

1. MVC 模式:最朴素的分离尝试

MVC 把应用分成三块:Model(数据)、View(界面)、Controller(控制器)。在 Android 里,Activity 通常既当 View 又当 Controller,这就埋下了隐患。

我遇到过最典型的场景:一个列表页面,数据加载、网络请求、界面刷新全写在 Activity 里。三个月后回来维护,自己都看不懂当初写的逻辑。

MVC 的问题在哪?说白了,View 和 Controller 耦合太紧。Activity 既要处理用户点击,又要管理数据状态,测试起来特别痛苦。

2. MVP 模式:接口驱动的解耦尝试

MVP 把 Controller 换成了 Presenter,核心变化是:View 和 Model 彻底隔离,所有交互都通过 Presenter 中转。

// Presenter 层
class LoginPresenter(private val view: LoginView) {
    fun login(username: String, password: String) {
        // 业务逻辑
        if (username.isNotEmpty() && password.isNotEmpty()) {
            view.showLoading()
            // 调用 Model 层
        }
    }
}

// View 接口
interface LoginView {
    fun showLoading()
    fun hideLoading()
    fun onLoginSuccess()
    fun onLoginError(msg: String)
}

MVP 的好处很明显:可测试性大大提升。我可以 mock 一个 View 接口,单独测试 Presenter 的逻辑。但坏处也来了——接口爆炸。每个页面都要定义 View 和 Presenter 的接口,项目大了以后,光接口文件就几十个。

我的经验:MVP 适合中小型项目,或者团队对测试要求比较高的场景。但如果页面交互复杂,Presenter 会变得臃肿,这时候就该考虑 MVVM 了。

3. MVVM 模式:数据驱动的现代方案

MVVM 引入了 ViewModel 和 LiveData(或者 StateFlow),核心思想是:数据变化自动驱动 UI 更新,开发者不需要手动操作 View。

// ViewModel 层
class LoginViewModel : ViewModel() {
    private val _loginState = MutableLiveData<LoginUiState>()
    val loginState: LiveData<LoginUiState> get() = _loginState

    fun login(username: String, password: String) {
        viewModelScope.launch {
            _loginState.value = LoginUiState.Loading
            try {
                val result = repository.login(username, password)
                _loginState.value = LoginUiState.Success(result)
            } catch (e: Exception) {
                _loginState.value = LoginUiState.Error(e.message)
            }
        }
    }
}

// Activity 中观察
viewModel.loginState.observe(this) { state ->
    when (state) {
        is LoginUiState.Loading -> showLoading()
        is LoginUiState.Success -> navigateToHome()
        is LoginUiState.Error -> showError(state.message)
    }
}

你想想看,MVVM 最大的优势是什么?生命周期感知。ViewModel 在屏幕旋转时不会丢失数据,LiveData 自动管理订阅关系,再也不用担心内存泄漏了。

注意:MVVM 不是银弹。如果滥用 LiveData,或者把太多逻辑塞进 ViewModel,照样会写出难以维护的代码。我曾经接手过一个项目,ViewModel 里塞了十几个 LiveData,一个方法三百行……嗯,那感觉就像回到了 MVC 时代。

二、Clean Architecture:让代码经得起时间考验

聊完三层架构的演进,咱们再来看一个更宏观的问题:项目大了以后,怎么保证不同模块之间不互相污染?

Clean Architecture 的核心思想其实就一句话:依赖方向必须由外向内。外层可以依赖内层,但内层绝对不能依赖外层。

1. 分层结构

典型的 Clean Architecture 分三层:

  • 数据层(Data Layer):负责网络请求、本地数据库、文件读写等。说白了就是「数据从哪来」。
  • 领域层(Domain Layer):包含业务逻辑和实体类。这一层是纯 Kotlin/Java,不依赖任何 Android 框架。
  • 展示层(Presentation Layer):就是 MVVM 里的 View 和 ViewModel,负责 UI 展示和用户交互。

我个人的习惯:领域层是整个项目的核心,一定要保持干净。所有第三方库、Android SDK 的依赖,都不能出现在这一层。这样即使以后换网络库、换数据库,业务逻辑完全不用动。

2. 依赖反转

Clean Architecture 用了一个很巧妙的设计:依赖反转。内层定义接口,外层去实现。举个例子:

// 领域层定义接口(不依赖任何具体实现)
interface UserRepository {
    suspend fun getUser(id: String): User
}

// 数据层实现接口
class UserRepositoryImpl(
    private val api: UserApi,
    private val dao: UserDao
) : UserRepository {
    override suspend fun getUser(id: String): User {
        // 先从缓存取,没有再从网络拉
        return dao.getUser(id) ?: api.fetchUser(id).also { dao.save(it) }
    }
}

这样做的好处是什么?领域层完全不知道数据是从网络来的还是从数据库来的。它只关心「给我一个 User 对象」,至于怎么来的,那是数据层的事。

3. 架构图

下面这张图展示了 Clean Architecture 的分层结构和依赖方向:

数据层 (Data Layer) 领域层 (Domain Layer) 展示层 (Presentation Layer) ViewModel LiveData / StateFlow UI 组件 (Activity / Fragment / Compose) 依赖方向 依赖方向 依赖方向由外向内,内层不依赖外层

三、MVVM + Clean Architecture:最佳实践组合

在实际项目中,我通常把 MVVM 和 Clean Architecture 结合起来用。说白了,MVVM 解决的是 UI 层的组织问题,Clean Architecture 解决的是整个项目的分层问题

1. 典型项目结构

app/
├── data/                    # 数据层
│   ├── remote/              # 网络数据源
│   ├── local/               # 本地数据源
│   └── repository/          # Repository 实现
├── domain/                  # 领域层
│   ├── model/               # 实体类
│   ├── repository/          # Repository 接口
│   └── usecase/             # 业务用例
└── presentation/            # 展示层
    ├── viewmodel/           # ViewModel
    ├── ui/                  # Activity / Fragment / Compose
    └── di/                  # 依赖注入

避坑指南:我曾经在项目里把 UseCase 层做得特别重,每个 UseCase 只做一件事,结果一个简单的登录流程拆了七八个 UseCase。后来发现过度抽象反而增加了理解成本。我的建议是:UseCase 不要为了拆而拆,当业务逻辑确实复杂到需要复用的时候再提取。

2. 数据流向

整个架构的数据流向是这样的:

  1. 用户点击按钮 → ViewModel 收到事件
  2. ViewModel 调用 UseCase(业务逻辑)
  3. UseCase 通过 Repository 接口获取数据
  4. Repository 实现类从网络或本地获取数据
  5. 数据原路返回,最终通过 LiveData/StateFlow 更新 UI

你发现没有?每一层只关心自己的事。ViewModel 不知道数据从哪来,Repository 不知道数据怎么展示。这就是分层架构的魅力。

四、总结

从 MVC 到 MVP 再到 MVVM,每一次演进都在解决之前架构的痛点。而 Clean Architecture 提供了一套更宏观的指导原则,让项目在长期迭代中保持健康。

说实话,没有完美的架构,只有适合当前项目的架构。但如果你问我个人建议:新项目直接用 MVVM + Clean Architecture,虽然前期搭建成本高一点,但后期维护的幸福感会提升很多。

记住核心原则:依赖方向由外向内,数据驱动 UI,每一层职责单一。做到这三点,你的项目架构就不会太差。


公众号:蓝海资料掘金营,微信deep3321