MVP 写法 → MVVM
老写法(Java — MVP)
// Contract
interface MainContract {
interface View {
void showUser(String name);
void showLoading();
void hideLoading();
void showError(String msg);
}
interface Presenter {
void loadUser();
void detach();
}
}
// Presenter
public class MainPresenter implements MainContract.Presenter {
private MainContract.View view;
public void attach(MainContract.View view) { this.view = view; }
@Override
public void loadUser() {
view.showLoading();
api.loadUser(new Callback<User>() {
@Override
public void onSuccess(User user) { view.showUser(user.getName()); view.hideLoading(); }
@Override
public void onError(String msg) { view.showError(msg); view.hideLoading(); }
});
}
@Override
public void detach() { view = null; }
}
问题在哪里
每个页面都要写 Contract 接口 + Presenter 实现,大量样板代码。Presenter 持有 View 引用需要手动 attach/detach,忘调用 detach 就会内存泄漏。测试 Presenter 需要 mock View 接口,如果接口有十几个方法就很痛苦。
新写法(MVVM + ViewModel)
// ViewModel — 不需要 Contract,不需要 View 接口
class MainViewModel(private val api: ApiService) : ViewModel() {
private val _userName = MutableLiveData<String>()
val userName: LiveData<String> = _userName
private val _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean> = _isLoading
fun loadUser() {
viewModelScope.launch {
_isLoading.value = true
try {
val user = withContext(Dispatchers.IO) { api.loadUser() }
_userName.value = user.name
} catch (e: Exception) {
_error.value = e.message
} finally {
_isLoading.value = false
}
}
}
}
一句话注意
从 MVP 切到 MVVM 最大的思维变化:Presenter 不主动调 View 的方法,而是 ViewModel 更新数据,View 自己观察数据变更来响应。这消除了 Presenter 对 View 的直接引用,不再需要 attach/detach 生命周期管理。
MVP 的 View 接口方法越多,MVVM 的收益越大——ViewModel 只暴露 LiveData/StateFlow 数据,不管谁在观察。View 层可以是一个 Activity、一个 Fragment、甚至是一个 Compose 页面,ViewModel 对它们一视同仁。
Java Android 老项目迁移系列,持续更新中。