深入理解 Android 架构:从 MVC、MVP 到 Jetpack MVVM 的进化之路

179 阅读5分钟

一、开发架构的本质:为什么需要架构设计?

开发架构就像盖房子的设计图,核心解决三个问题:

  1. 职责划分:让每个模块只做自己的事(如厨房只做饭,卧室只睡觉)

  2. 解耦:模块间关系简单,一个模块修改不影响其他模块(如厨房水管坏了不影响卧室)

  3. 可维护性:代码结构清晰,新人能快速接手,修改 bug 不牵一发动全身

举个生活例子:

  • MVC:像家庭主妇一人包办买菜、做饭、端菜,累到崩溃(Activity 同时干 View 和 Controller 的活)
  • MVP:丈夫负责买菜(Model),妻子负责做饭(Presenter),孩子端菜(View),但妻子和孩子互相依赖(Presenter 和 View 互相持有)
  • MVVM:全家通过微信群(数据)沟通,丈夫买菜后发群,妻子做饭后发群,孩子看群更新端菜,谁也不直接指挥谁(数据驱动,解耦彻底)

二、Android 架构的进化史:从混乱到解耦

2.1 MVC:Android 原生的 "混乱时代"
  • 问题场景:Activity 既管界面(View)又写逻辑(Controller),像一个人同时开车、导航、打电话,代码量爆炸(一个 Activity 几千行常见)。

  • 核心痛点

    • Activity 变成 "上帝类",违反单一职责原则
    • Model 直接操作 View(如网络请求后直接textView.setText()),耦合严重
2.2 MVP:接口隔离的 "分工时代"
  • 改进点:把 Activity 的逻辑抽离到 Presenter,View 只负责显示。

  • 典型问题

    • 大量接口(IView、IPresenter)导致代码膨胀,像公司部门太多流程复杂
    • Presenter 持有 View 引用,容易内存泄漏(如 Activity 旋转时 Presenter 没释放)
2.3 MVVM:数据驱动的 "智能时代"
  • 核心思想:用数据作为 "桥梁",View 自动观察数据变化,无需主动调用更新方法。

  • 类比场景:智能家居系统,温度传感器(Model)数据变化时,空调(View)自动调整,无需人工控制。

  • 关键优势

    • 彻底解耦:ViewModel 不持有 View,像主播只负责直播,不管观众是否在线
    • 数据驱动:数据变化自动更新 UI,避免 "回调地狱"
    • 配置变更友好:屏幕旋转时 ViewModel 数据不丢失,像手机切换横竖屏时视频继续播放

三、Jetpack MVVM:Google 官方的 "标准解法"

Google 用 Jetpack 组件实现 MVVM,核心组件如下:

  • ViewModel:持有 UI 数据,配置变更不丢失(如屏幕旋转时数据仍在)
  • LiveData:可观察的数据容器,自动感知生命周期(Activity 销毁时自动停止更新)
  • Repository:数据仓库,统一管理本地 / 网络数据来源
3.1 实战案例:用户列表页面的 MVVM 实现
第一步:构建 ViewModel(数据管理者)

java

public class UserListViewModel extends ViewModel {
    // 用LiveData包装数据,变化时自动通知View
    private MutableLiveData<List<User>> userListLiveData = new MutableLiveData<>();
    private MutableLiveData<Boolean> loadingLiveData = new MutableLiveData<>();
    
    // 暴露不可变的LiveData,防止外部篡改
    public LiveData<List<User>> getUserList() { return userListLiveData; }
    public LiveData<Boolean> getLoading() { return loadingLiveData; }
    
    // 调用Repository获取数据
    public void fetchUserList() {
        loadingLiveData.setValue(true);
        UserRepository.getInstance().getUsersFromServer(
            users -> {
                loadingLiveData.setValue(false);
                userListLiveData.setValue(users);
            },
            error -> {
                loadingLiveData.setValue(false);
                userListLiveData.setValue(null);
            }
        );
    }
}
第二步:Activity 作为 View(观察者)

java

public class UserListActivity extends AppCompatActivity {
    private UserListViewModel viewModel;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 获取ViewModel(配置变更时返回同一个实例)
        viewModel = new ViewModelProvider(this).get(UserListViewModel.class);
        // 观察数据变化,自动更新UI
        viewModel.getUserList().observe(this, users -> {
            if (users != null) {
                adapter.setData(users);
            } else {
                Toast.makeText(this, "加载失败", Toast.LENGTH_SHORT).show();
            }
        });
        viewModel.getLoading().observe(this, isLoading -> {
            progressBar.setVisibility(isLoading ? View.VISIBLE : View.GONE);
        });
        // 触发数据加载
        viewModel.fetchUserList();
    }
}
第三步:Repository(数据源头统一管理)

java

public class UserRepository {
    private UserApiService apiService;
    private UserDatabase database;
    
    // 单一可信来源:优先返回本地数据,同时异步更新网络数据
    public void getUsersFromServer(Callback<List<User>> callback) {
        // 先查本地缓存
        List<User> cachedUsers = database.userDao().getAll();
        if (!cachedUsers.isEmpty()) {
            callback.onSuccess(cachedUsers); // 先显示本地数据
        }
        
        // 异步获取网络数据
        new AsyncTask<Void, Void, List<User>>() {
            @Override
            protected List<User> doInBackground(Void... voids) {
                try {
                    // 模拟网络请求
                    List<User> remoteUsers = apiService.getUsers().execute().body();
                    // 网络数据更新到本地
                    database.userDao().insertAll(remoteUsers);
                    return remoteUsers;
                } catch (Exception e) {
                    return null;
                }
            }
            
            @Override
            protected void onPostExecute(List<User> users) {
                if (users != null) {
                    callback.onSuccess(users); // 网络数据返回后更新UI
                } else {
                    callback.onFailed("网络错误");
                }
            }
        }.execute();
    }
}
3.2 核心原理:为什么 Jetpack MVVM 能避免常见坑?
  1. 配置变更安全:ViewModel 在 Activity 重建(如旋转屏幕)时不会销毁,数据自动保留,避免重复请求网络。
  2. 生命周期感知:LiveData 只在 Activity 处于活跃状态(onStart-onStop 之间)更新 UI,避免 Activity 销毁后调用导致的空指针。
  3. 单一可信来源:Repository 统一管理数据来源,确保本地缓存和网络数据的一致性,如先显示本地数据再更新网络,提升用户体验。

四、从 MVP 到 MVVM 的平滑过渡

如果现有项目是 MVP 架构,改造步骤如下:

  1. 断舍离:删除 Presenter 中对 View 的引用(如view.showLoading()

  2. 数据化:将 Presenter 的逻辑转移到 ViewModel,返回值改为 LiveData

  3. 观察者模式:Activity/Fragment 不再调用 Presenter 的更新方法,而是观察 LiveData

  4. 仓库化:提取 Model 中的数据获取逻辑到 Repository,统一管理数据源

举个例子:
MVP 中 Presenter 的方法:

java

void loadUserList() { view.showLoading(); model.fetchUsers(this::onSuccess, this::onError); }

改造为 MVVM 的 ViewModel:

java

LiveData<List<User>> loadUserList() {
    loadingLiveData.setValue(true);
    repository.fetchUsers(
        users -> {
            loadingLiveData.setValue(false);
            userListLiveData.setValue(users);
        },
        error -> {
            loadingLiveData.setValue(false);
            userListLiveData.setValue(null);
        }
    );
    return userListLiveData;
}

五、为什么没讲 DataBinding?MVVM 的本质与工具的区别

文档刻意没提 DataBinding,因为:

  • MVVM 的核心是数据驱动:即使不用 DataBinding,通过 LiveData 观察数据更新 UI 也已经是完整的 MVVM
  • DataBinding 是辅助工具:它能简化 UI 和数据的绑定(如TextView.setText(viewModel.name)可写成android:text="@={viewModel.name}"),但存在争议(如布局文件逻辑复杂、调试困难)
  • Google 官方架构指南也未强制:在 Jetpack 中,DataBinding 是可选的,而非 MVVM 的必要条件

六、总结:MVVM 的核心价值

  • 解耦:View、ViewModel、Model 三层独立,修改一层不影响其他层,如更换 UI 样式时无需修改逻辑

  • 可测试性:ViewModel 可以脱离 UI 进行单元测试,像测试一个普通 Java 类

  • 用户体验优化:数据持久化(配置变更不丢失)、离线数据展示、自动更新 UI,提升应用响应速度

记住:架构不是银弹,选择适合项目的才是最好的。Jetpack MVVM 通过组件化的方式,将复杂的架构设计转化为可复用的工具,让开发者更专注于业务逻辑,这正是它的魅力所在。