一、开发架构的本质:为什么需要架构设计?
开发架构就像盖房子的设计图,核心解决三个问题:
-
职责划分:让每个模块只做自己的事(如厨房只做饭,卧室只睡觉)
-
解耦:模块间关系简单,一个模块修改不影响其他模块(如厨房水管坏了不影响卧室)
-
可维护性:代码结构清晰,新人能快速接手,修改 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 能避免常见坑?
- 配置变更安全:ViewModel 在 Activity 重建(如旋转屏幕)时不会销毁,数据自动保留,避免重复请求网络。
- 生命周期感知:LiveData 只在 Activity 处于活跃状态(onStart-onStop 之间)更新 UI,避免 Activity 销毁后调用导致的空指针。
- 单一可信来源:Repository 统一管理数据来源,确保本地缓存和网络数据的一致性,如先显示本地数据再更新网络,提升用户体验。
四、从 MVP 到 MVVM 的平滑过渡
如果现有项目是 MVP 架构,改造步骤如下:
-
断舍离:删除 Presenter 中对 View 的引用(如
view.showLoading()) -
数据化:将 Presenter 的逻辑转移到 ViewModel,返回值改为 LiveData
-
观察者模式:Activity/Fragment 不再调用 Presenter 的更新方法,而是观察 LiveData
-
仓库化:提取 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 通过组件化的方式,将复杂的架构设计转化为可复用的工具,让开发者更专注于业务逻辑,这正是它的魅力所在。