You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
        });
    }
}

方案优势

  1. 符合MVVM架构原则:依赖方向完全正确,Repository不依赖上层组件,低耦合、易测试。
  2. 生命周期安全:LiveData会自动感知UI的生命周期,不会导致内存泄漏,在UI销毁时自动取消观察。
  3. 状态统一管理:通过Resource类可以清晰处理加载、成功、错误三种状态,UI逻辑更清晰。

内容的提问来源于stack exchange,提问作者Amir Dora.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 06:23:10