MVVM架构下如何在Volley请求成功时通知UI?
正确的MVVM方案:通过LiveData传递Volley请求结果到UI
你说得对,在Repository里实例化ViewModel绝对不是好实践——这完全违反了MVVM的依赖方向(应该是 View → ViewModel → Repository,底层组件不应该依赖上层),会导致强耦合、难以测试和维护。下面给你一套标准的、符合MVVM架构的解决方案,核心思路是让Repository通过LiveData把请求状态/结果传递给ViewModel,再由ViewModel暴露给UI层观察。
步骤1:在Repository中定义LiveData传递状态
首先在AppRepository里添加一个MutableLiveData,用来封装请求的加载、成功、错误状态(推荐用一个通用的Resource类来统一包装,这样能更清晰地处理不同状态)。
先定义通用的Resource状态类
这个类用来统一封装网络请求的状态和数据,是MVVM中处理异步请求的常用模式:
public class Resource<T> { @NonNull public final Status status; @Nullable public final T data; @Nullable public final String message; private Resource(@NonNull Status status, @Nullable T data, @Nullable String message) { this.status = status; this.data = data; this.message = message; } // 成功状态,携带返回数据 public static <T> Resource<T> success(@NonNull T data) { return new Resource<>(Status.SUCCESS, data, null); } // 错误状态,携带错误信息 public static <T> Resource<T> error(@Nullable String msg, @Nullable T data) { return new Resource<>(Status.ERROR, data, msg); } // 加载状态,可携带临时数据 public static <T> Resource<T> loading(@Nullable T data) { return new Resource<>(Status.LOADING, data, null); } public enum Status { SUCCESS, ERROR, LOADING } }
修改AppRepository,添加LiveData并更新回调
public class AppRepository { private static final String APP_REPOSITORY_TAG = "AppRepository"; private Context context; private Executor executor; // 用来传递更新请求的状态和结果 private MutableLiveData<Resource<JSONObject>> updateEventPersonLiveData = new MutableLiveData<>(); // 构造方法等原有代码... // 对外暴露不可变的LiveData,防止外部直接修改 public LiveData<Resource<JSONObject>> getUpdateEventPersonLiveData() { return updateEventPersonLiveData; } public void updateEventPersonEntity(EventPersonsEntity eventPersonsEntity) { executor.execute(() -> { // 先发送加载状态,让UI显示加载中 updateEventPersonLiveData.postValue(Resource.loading(null)); if (isNetworkAvailable(context)) { post_updateEventPersonEntity(eventPersonsEntity); } else { // 网络不可用,发送错误状态 updateEventPersonLiveData.postValue(Resource.error("网络不可用,请检查连接", null)); } }); } private void post_updateEventPersonEntity(EventPersonsEntity eventPersonsEntity) { // 原有参数构造代码... JSONObject objRegData = new JSONObject(params); String eventPersonApi = APP_URL.EVENT_PERSONS_API + eventPersonsEntity.getEventPersonId(); RequestQueueSingleton.getInstance(context).objectRequest( eventPersonApi, Request.Method.PUT, this::onSuccess_updateEventPersonEntity, this::onError, objRegData ); } private void onError(VolleyError error) { Log.d(APP_REPOSITORY_TAG, "requestError: " + error); String errorMsg = error.getMessage() != null ? error.getMessage() : "请求失败"; // 发送错误状态到LiveData updateEventPersonLiveData.postValue(Resource.error(errorMsg, null)); } private void onSuccess_updateEventPersonEntity(JSONObject jsonObject) { // 发送成功状态和返回数据到LiveData updateEventPersonLiveData.postValue(Resource.success(jsonObject)); } }
步骤2:ViewModel中转LiveData给UI
ViewModel的职责是作为View和Repository的中间层,只需要把Repository的LiveData暴露给UI即可,不需要额外处理逻辑:
public class CheckInViewModel extends ViewModel { private AppRepository mRepository; private LiveData<Resource<JSONObject>> updateEventPersonLiveData; public CheckInViewModel(AppRepository repository) { mRepository = repository; updateEventPersonLiveData = mRepository.getUpdateEventPersonLiveData(); } public void updateEventPersonEntity(EventPersonsEntity eventPersonsEntity) { mRepository.updateEventPersonEntity(eventPersonsEntity); } // 对外暴露LiveData,供UI层观察 public LiveData<Resource<JSONObject>> getUpdateEventPersonLiveData() { return updateEventPersonLiveData; } }
步骤3:UI层(Fragment/Activity)观察LiveData
在Fragment中观察ViewModel的LiveData,根据不同状态更新UI:
public class CheckInFragment extends Fragment { private CheckInViewModel checkInViewModel; @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // 初始化ViewModel(建议用ViewModelProvider,确保生命周期安全) checkInViewModel = new ViewModelProvider(this).get(CheckInViewModel.class); // 观察更新请求的状态 checkInViewModel.getUpdateEventPersonLiveData().observe(getViewLifecycleOwner(), resource -> { switch (resource.status) { case LOADING: // 显示加载UI,比如ProgressBar progressBar.setVisibility(View.VISIBLE); break; case SUCCESS: // 请求成功,更新UI:隐藏加载、显示成功提示、刷新数据等 progressBar.setVisibility(View.GONE); Toast.makeText(getContext(), "数据更新成功", Toast.LENGTH_SHORT).show(); JSONObject responseData = resource.data; // 可以在这里处理返回的JSON数据 break; case ERROR: // 请求失败,显示错误提示 progressBar.setVisibility(View.GONE); Toast.makeText(getContext(), resource.message, Toast.LENGTH_SHORT).show(); break; } }); // 触发更新的按钮点击事件 btnSave.setOnClickListener(v -> { // 构建EventPersonsEntity对象 EventPersonsEntity entity = new EventPersonsEntity(); // 填充实体数据... checkInViewModel.updateEventPersonEntity(entity); }); } }
方案优势
- 符合MVVM架构原则:依赖方向完全正确,Repository不依赖上层组件,低耦合、易测试。
- 生命周期安全:LiveData会自动感知UI的生命周期,不会导致内存泄漏,在UI销毁时自动取消观察。
- 状态统一管理:通过
Resource类可以清晰处理加载、成功、错误三种状态,UI逻辑更清晰。
内容的提问来源于stack exchange,提问作者Amir Dora.
相关产品推荐
相关产品推荐

