第一章:架构演进史:从MVC到MVP再到MVVM,为什么我们要不断重构?

说实话,我入行那会儿,Android开发还处在“野蛮生长”的阶段。

那时候写代码,Activity里塞满了几百行甚至上千行的逻辑。网络请求、数据解析、UI更新,全揉在一起。能跑就行,谁管什么架构?

但项目一复杂,问题就来了。改一个UI,可能牵动整个网络层。加一个新功能,得把Activity翻个底朝天。测试?那更是噩梦。

所以,架构的出现,本质上是为了解决一个问题:让代码变得好改、好测、好维护

1.1 MVC:Android原生的“混乱”起点

MVC(Model-View-Controller)是最早被引入Android的架构思想。

  • Model:数据层,负责业务逻辑和数据处理。
  • View:视图层,对应XML布局和Activity/Fragment。
  • Controller:控制层,在Android里,Activity/Fragment天然承担了这个角色。

听起来挺合理,对吧?但实际用起来,问题很大。

Activity既是View又是Controller,职责严重耦合。我早期做过一个电商App,一个商品详情Activity写了快2000行。里面既有UI初始化,又有网络请求回调,还有数据缓存逻辑。每次需求变更,我都得小心翼翼,生怕改崩了哪个角落。

MVC的核心痛点:

  • Activity/Fragment过于臃肿,难以维护
  • View和Controller耦合,单元测试困难
  • 业务逻辑分散,复用性差

我记得有一次,产品经理说要改商品列表的排序逻辑。我翻了半天代码,才发现排序逻辑散落在三个不同的回调里。改完一个,忘了改另一个,结果线上出了bug。嗯,从那以后,我就开始认真思考架构问题了。

3.2 MVP:解耦的第一步,但代价不小

MVP(Model-View-Presenter)的出现,算是Android架构的一次重要进化。

它把Controller换成了Presenter,并且明确了职责:View只负责UI展示,Presenter负责业务逻辑。View和Presenter通过接口通信,实现了真正的解耦。

// View接口
public interface ILoginView {
    void showLoading();
    void hideLoading();
    void onLoginSuccess(User user);
    void onLoginError(String msg);
}

// Presenter
public class LoginPresenter {
    private ILoginView mView;
    private LoginModel mModel;

    public LoginPresenter(ILoginView view) {
        this.mView = view;
        this.mModel = new LoginModel();
    }

    public void login(String username, String password) {
        mView.showLoading();
        mModel.login(username, password, new Callback<User>() {
            @Override
            public void onSuccess(User user) {
                mView.hideLoading();
                mView.onLoginSuccess(user);
            }

            @Override
            public void onError(String msg) {
                mView.hideLoading();
                mView.onLoginError(msg);
            }
        });
    }
}

你看,这样写,逻辑清晰多了。测试也方便,可以轻松mock View接口。

但MVP也有它的毛病。最明显的就是:接口爆炸。每个页面都要定义View和Presenter接口,项目大了,接口文件多到让人头疼。而且Presenter里持有View的引用,如果忘记在onDestroy里释放,很容易内存泄漏。

我的经验:用MVP时,我习惯在BasePresenter里做生命周期绑定,用弱引用持有View,避免内存泄漏。但说实话,每次都要手动管理,挺烦的。

3.3 MVVM:数据驱动的优雅方案

MVVM(Model-View-ViewModel)的出现,算是解决了MVP的大部分痛点。

它的核心思想是:数据驱动UI。ViewModel通过LiveData或StateFlow持有数据,View观察这些数据的变化,自动更新UI。ViewModel不持有View的引用,彻底解决了内存泄漏问题。

// ViewModel
public class LoginViewModel extends ViewModel {
    private MutableLiveData<User> userLiveData = new MutableLiveData<>();
    private MutableLiveData<String> errorLiveData = new MutableLiveData<>();
    private LoginRepository repository;

    public LoginViewModel() {
        repository = new LoginRepository();
    }

    public void login(String username, String password) {
        repository.login(username, password, new Callback<User>() {
            @Override
            public void onSuccess(User user) {
                userLiveData.postValue(user);
            }

            @Override
            public void onError(String msg) {
                errorLiveData.postValue(msg);
            }
        });
    }

    public LiveData<User> getUserLiveData() {
        return userLiveData;
    }

    public LiveData<String> getErrorLiveData() {
        return errorLiveData;
    }
}

// Activity中观察
loginViewModel.getUserLiveData().observe(this, user -> {
    // 自动更新UI
    updateUI(user);
});

你想想看,ViewModel不需要知道View的存在,它只管提供数据。View只管观察数据变化。职责清晰,测试也简单。

而且ViewModel在屏幕旋转时不会丢失数据,这一点比Presenter强太多了。

MVVM的优势:

  • 数据驱动,UI自动更新
  • ViewModel不持有View引用,无内存泄漏
  • 生命周期感知,自动管理订阅
  • 更易于单元测试

3.4 为什么我们要不断重构?

讲到这里,你可能会问:既然MVVM这么好,为什么还要提MVC和MVP?

原因很简单:没有银弹

每个架构都有它的适用场景。MVC适合简单页面,MVP适合逻辑复杂的页面,MVVM则适合数据驱动型页面。但更重要的是,重构不是为了追求时髦,而是为了解决实际问题

我曾经接手过一个老项目,用的是MVC,Activity里全是逻辑。我花了两个月,逐步把核心模块重构到MVVM。过程很痛苦,但重构完后,新功能开发效率提升了至少30%,bug率也明显下降。

注意:重构不是重写。不要为了用MVVM而强行重构。如果项目稳定、需求变化少,保持现状也是一种选择。重构的时机,应该是当你觉得“改不动了”的时候。

说白了,架构演进史,就是一部对抗复杂性的历史。从MVC到MVP,我们学会了解耦。从MVP到MVVM,我们学会了数据驱动。未来可能还会有MVI、Compose等新架构,但核心目标不变:让代码更清晰、更可测、更易维护

我个人习惯,新项目直接上MVVM + Jetpack。老项目重构,则根据模块复杂度,选择MVP或MVVM。记住,架构是工具,不是目的。适合你的,才是最好的。

架构 核心思想 优点 缺点
MVC Model-View-Controller 简单直接,适合小项目 Activity臃肿,耦合严重
MVP Model-View-Presenter 解耦清晰,易于测试 接口爆炸,内存泄漏风险
MVVM Model-View-ViewModel 数据驱动,生命周期安全 学习曲线稍高,调试略复杂

嗯,这一章就到这里。下一章,我会带你深入MVVM的核心组件,看看LiveData、ViewModel和Repository到底该怎么配合,才能写出真正可维护的代码。