同一个功能:加载用户并显示名字
功能不变:
点按钮 → 请求用户 → 显示用户名 → 失败提示
一、❌ 传统 Activity 写法(所有东西都在 Activity)
Activity(UI类)
public class UserActivity extends AppCompatActivity {
TextView tvName;
Button btnLoad;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user);
tvName = findViewById(R.id.tvName);
btnLoad = findViewById(R.id.btnLoad);
btnLoad.setOnClickListener(v -> {
// ① UI 事件
loadUser();
});
}
private void loadUser() {
// ② 业务逻辑
showLoading();
Api.getUser(new Callback<User>() {
@Override
public void onSuccess(User user) {
// ③ 数据处理
tvName.setText(user.getName()); // 改 UI
hideLoading();
}
@Override
public void onError(String msg) {
// ④ 错误处理
Toast.makeText(UserActivity.this, msg, Toast.LENGTH_SHORT).show();
hideLoading();
}
});
}
}
❗问题在哪?
| 问题 | 原因 |
|---|---|
| Activity 很胖 | 所有职责都在 |
| 不可测试 | 依赖 Android |
| UI & 逻辑耦合 | 直接操作 View |
| 状态不可控 | loading / error 到处写 |
二、✅ 重构目标(先不看代码)
我们要做到:
Activity:我只管展示
ViewModel:我管逻辑和状态
Activity 不再知道:
-
请求怎么发
-
什么时候成功 / 失败
-
状态怎么切换
三、✅ MVVM 写法(拆开)
1️⃣ ViewModel:专门"管状态"
public class UserViewModel extends ViewModel {
// 用户名状态
private MutableLiveData<String> userName = new MutableLiveData<>();
// loading 状态
private MutableLiveData<Boolean> loading = new MutableLiveData<>();
// 错误信息
private MutableLiveData<String> errorMsg = new MutableLiveData<>();
public LiveData<String> getUserName() {
return userName;
}
public LiveData<Boolean> getLoading() {
return loading;
}
public LiveData<String> getErrorMsg() {
return errorMsg;
}
public void loadUser() {
loading.setValue(true);
Api.getUser(new Callback<User>() {
@Override
public void onSuccess(User user) {
userName.setValue(user.getName()); // 只改数据
loading.setValue(false);
}
@Override
public void onError(String msg) {
errorMsg.setValue(msg); // 只抛状态
loading.setValue(false);
}
});
}
}
📌 注意一个关键点:
ViewModel 从头到尾没有一句 UI 代码
2️⃣ Activity:变成"数据监听器"
public class UserActivity extends AppCompatActivity {
private UserViewModel viewModel;
private TextView tvName;
private ProgressBar progressBar;
private Button btnLoad;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user);
tvName = findViewById(R.id.tvName);
progressBar = findViewById(R.id.progressBar);
btnLoad = findViewById(R.id.btnLoad);
viewModel = new ViewModelProvider(this).get(UserViewModel.class);
// ① 监听数据
viewModel.getUserName().observe(this, name -> {
tvName.setText(name);
});
viewModel.getLoading().observe(this, show -> {
progressBar.setVisibility(show ? View.VISIBLE : View.GONE);
});
viewModel.getErrorMsg().observe(this, msg -> {
if (msg != null) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
});
// ② 把点击事件"交出去"
btnLoad.setOnClickListener(v -> {
viewModel.loadUser();
});
}
}
四、最重要的一行对比(🔥核心)
以前
tvName.setText(user.getName());
现在
userName.setValue(user.getName());
👉 UI 更新从"命令式"变成"数据驱动"
五、用一句话理解"重构发生了什么"
Activity 不再"做事"
ViewModel 不再"碰 UI"
它们通过"状态"交流
六、责任变化对照表(一定要看)
| 职责 | 以前 | 现在 |
|---|---|---|
| 点击处理 | Activity | Activity(转交) |
| 网络请求 | Activity | ViewModel |
| 成功失败判断 | Activity | ViewModel |
| UI 更新 | Activity | Activity |
| 状态来源 | Activity | ViewModel |
七、已经"半只脚进 MVVM 了"的标志
如果现在能理解这句话:
Activity 不知道"什么时候该改 UI",
它只知道"数据变了我就展示"
那已经抓住 MVVM 的核心原理 了。