MVVM (Model-View-ViewModel) 是一种架构设计模式,将应用程序分为三个主要部分:
职责:
在你的项目中的体现:
java// TestModel.java - Model 层
public class TestModel implements TestManager.ILoadDataCallBack {
private IDataFileViewModel mIDataFileViewModel;
// 单例模式,管理数据访问
public static TestModel getInstance() {
return SingleHolder.sInstance;
}
// 加载数据
public void load(String tag) {
TestManager.getInstance().load(tag);
}
// 数据加载成功后的回调
@Override
public void loadDataFileSuccess() {
// 通知 ViewModel 数据已更新
mIDataFileViewModel.onDataFileListUpdate(
TestManager.getInstance().getDataFileList()
);
}
}
特点:
职责:
在你的项目中的体现:
java// TestCaseListFragment.java - View 层
public class TestCaseListFragment extends Fragment {
private DataFileViewModel viewModel; // 持有 ViewModel 引用
private DataFileAdapter adapter;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 获取 ViewModel 实例
viewModel = new ViewModelProvider(requireActivity())
.get(DataFileViewModel.class);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// 观察数据文件列表的变化
viewModel.getCurrentDataFiles().observe(
getViewLifecycleOwner(),
dataFiles -> {
// 当数据变化时,自动更新 UI
if (dataFiles != null) {
adapter.updateData(dataFiles);
updateStatistics(dataFiles);
}
}
);
// 观察正在执行的文件
viewModel.getExecutingDataFile().observe(
getViewLifecycleOwner(),
script -> {
if (script != null) {
// 更新对应项的 UI
int position = adapter.getDataFiles().indexOf(script);
if (position != -1) {
adapter.notifyItemChanged(position);
}
}
}
);
}
// 用户点击执行按钮
@Override
public void onExecuteClick(DataFileItem dataFileItem) {
// 通过 ViewModel 执行操作
long executionId = viewModel.startTestSync();
// ...
}
}
特点:
observe() 观察 ViewModel 的数据getViewLifecycleOwner())职责:
在你的项目中的体现:
java// DataFileViewModel.java - ViewModel 层
public class DataFileViewModel extends ViewModel
implements IDataFileViewModel {
// 使用 LiveData 暴露数据给 View
private final MutableLiveData<List<DataFileItem>> _currentDataFiles
= new MutableLiveData<>();
public LiveData<List<DataFileItem>> currentDataFiles = _currentDataFiles;
private TestModel testModel; // 持有 Model 引用
// 加载数据
public void load(String tag) {
testModel = TestModel.getInstance();
testModel.setIDataFileViewModel(this); // 设置回调
testModel.load(tag); // 请求 Model 加载数据
testModel.update();
}
// Model 层回调:数据更新
@Override
public void onDataFileListUpdate(List<DataFileItem> dataFileList) {
// 更新 LiveData,自动通知所有观察者(View)
_currentDataFiles.postValue(dataFileList);
loading.postValue(false);
}
// View 层调用:执行测试
public long startTestSync() {
return testModel.startTestSync();
}
}
特点:
┌─────────────────────────────────────────────────────────┐ │ MVVM 数据流向 │ └─────────────────────────────────────────────────────────┘ 用户操作 ↓ ┌──────┐ │ View │ (Fragment/Activity) └──┬───┘ │ 1. 用户点击按钮 │ 2. 调用 ViewModel 方法 ↓ ┌─────────────┐ │ ViewModel │ (DataFileViewModel) └──┬──────┬───┘ │ │ │ 3. │ 4. 通过回调接口 │ 调用 │ 接收数据更新 ↓ ↓ ┌──────┐ ┌──────┐ │Model │ │Model │ (TestModel) └──┬───┘ └──┬───┘ │ │ │ 5. │ 6. 数据加载完成 │ 加载 │ 回调 ViewModel │ 数据 │ ↓ ↓ ┌─────────────┐ │ Data Source │ (TestManager/网络/数据库) └─────────────┘ 反向数据流(响应式更新): Model → ViewModel → View (通过 LiveData)
java// MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 1. 获取 ViewModel
viewModel = new ViewModelProvider(this)
.get(DataFileViewModel.class);
// 2. 观察数据变化
viewModel.getCurrentDataFiles().observe(this, dataFiles -> {
// 当数据到达时,自动更新 UI
adapter.updateData(dataFiles);
});
// 3. 请求加载数据
viewModel.load("remote");
}
java// DataFileViewModel.java
public void load(String tag) {
// 1. 获取 Model 实例
testModel = TestModel.getInstance();
// 2. 设置回调接口(用于接收数据)
testModel.setIDataFileViewModel(this);
// 3. 请求 Model 加载数据
testModel.load(tag);
testModel.update();
}
java// TestModel.java
public void load(String tag) {
// 委托给 TestManager 加载数据(可能是网络请求或数据库查询)
TestManager.getInstance().load(tag);
}
// TestManager 异步加载数据后,通过回调通知
@Override
public void loadDataFileSuccess() {
// 数据加载成功,通知 ViewModel
mIDataFileViewModel.onDataFileListUpdate(
TestManager.getInstance().getDataFileList()
);
}
java// DataFileViewModel.java
@Override
public void onDataFileListUpdate(List<DataFileItem> dataFileList) {
// 更新 LiveData,触发所有观察者
_currentDataFiles.postValue(dataFileList);
loading.postValue(false); // 隐藏加载状态
}
java// MainActivity.java 中的观察者自动被触发
viewModel.getCurrentDataFiles().observe(this, dataFiles -> {
// 这里自动执行,更新 RecyclerView
adapter.updateData(dataFiles);
});
LiveData 是 Android 中实现数据绑定的关键组件:
java// ViewModel 中定义
private final MutableLiveData<List<DataFileItem>> _currentDataFiles
= new MutableLiveData<>();
public LiveData<List<DataFileItem>> currentDataFiles = _currentDataFiles;
// View 中观察
viewModel.getCurrentDataFiles().observe(lifecycleOwner, dataFiles -> {
// 数据变化时自动执行
updateUI(dataFiles);
});
优势:
postValue() 可在后台线程调用用户操作 → View → ViewModel → Model → 数据源 ↓ UI 更新 ← View ← ViewModel ← Model ← 数据更新
特点:
java// ViewModel 生命周期独立于 View
public class DataFileViewModel extends ViewModel {
@Override
protected void onCleared() {
super.onCleared();
// Activity/Fragment 销毁时调用
// 清理资源,避免内存泄漏
testModel.clear();
}
}
优势:
| 特性 | MVVM | MVP |
|---|---|---|
| View 与 Presenter/ViewModel | View 观察 ViewModel | View 持有 Presenter 引用 |
| 数据绑定 | LiveData/DataBinding | 手动调用 View 方法 |
| 耦合度 | 低(View 不持有 ViewModel) | 中(View 持有 Presenter) |
| 测试性 | 高(ViewModel 独立测试) | 高(Presenter 独立测试) |
| 特性 | MVVM | MVC |
|---|---|---|
| Controller | ViewModel | Controller |
| View 职责 | 仅 UI 展示 | UI + 部分逻辑 |
| 数据流向 | 单向,清晰 | 可能双向,复杂 |
❌ 错误示例:
javapublic class DataFileViewModel extends ViewModel {
private Activity activity; // 错误!会导致内存泄漏
}
✅ 正确做法:
javapublic class DataFileViewModel extends ViewModel {
// 使用 LiveData 暴露数据,不持有 View 引用
private final MutableLiveData<List<DataFileItem>> _dataFiles
= new MutableLiveData<>();
}
❌ 错误示例:
javapublic void loadData() {
// 不应该在 ViewModel 中处理 UI
Toast.makeText(context, "Loading...", Toast.LENGTH_SHORT).show();
}
✅ 正确做法:
java// ViewModel 中
private final MutableLiveData<Boolean> loading = new MutableLiveData<>();
public void loadData() {
loading.postValue(true);
// ...
}
// View 中观察
viewModel.getLoading().observe(this, isLoading -> {
if (isLoading) {
showLoadingDialog();
} else {
hideLoadingDialog();
}
});
postValue() 在后台线程更新setValue() 在主线程更新MutableLiveData┌─────────────────────────────────────┐ │ View 层 │ │ - MainActivity │ │ - TestCaseListFragment │ │ - DataFileAdapter │ └──────────────┬──────────────────────┘ │ observe LiveData │ 调用 ViewModel 方法 ↓ ┌─────────────────────────────────────┐ │ ViewModel 层 │ │ - DataFileViewModel │ │ - 管理 LiveData │ │ - 实现 IDataFileViewModel │ │ - 处理 UI 状态 │ └──────────────┬──────────────────────┘ │ 调用 Model 方法 │ 接收 Model 回调 ↓ ┌─────────────────────────────────────┐ │ Model 层 │ │ - TestModel (单例) │ │ - 封装业务逻辑 │ │ - 管理数据访问 │ │ - 回调 ViewModel │ └──────────────┬──────────────────────┘ │ 调用 Manager ↓ ┌─────────────────────────────────────┐ │ Data Source 层 │ │ - TestManager │ │ - 网络请求/数据库访问 │ └─────────────────────────────────────┘
javaviewModel.getCurrentDataFiles().observe(lifecycleOwner, data -> {
// 自动更新 UI
});
javaviewModel.load("remote");
javaviewModel.startTestSync();
java@Override
public void onDataFileListUpdate(List<DataFileItem> list) {
_currentDataFiles.postValue(list);
}
MVVM 架构通过以下方式实现清晰的代码组织:
MVVM 架构使得:
本文作者:lixf6
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!