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

将Google的NetworkBoundResource类重构为使用RxJava替代LiveData

嘿,关于Android架构组件里抽象网络数据获取逻辑的NetworkBoundResource,我来给你详细拆解下——这可是构建响应式网络请求的核心框架,用LiveData实现了一套完整的缓存读取→网络判断→数据同步的标准化流程,能帮你大幅减少重复的网络请求代码。

核心实现代码

首先放完整的抽象类代码(补全了你提供的片段):

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MediatorLiveData;
import java.util.Objects;

public abstract class NetworkBoundResource<ResultType, RequestType> {
    private final AppExecutors appExecutors;
    private final MediatorLiveData<Resource<ResultType>> result = new MediatorLiveData<>();

    protected NetworkBoundResource(AppExecutors appExecutors) {
        this.appExecutors = appExecutors;
        init();
    }

    private void init() {
        // 先发送加载状态给UI层
        result.setValue(Resource.loading(null));
        // 第一步:读取本地数据库缓存
        LiveData<ResultType> dbSource = loadFromDb();
        result.addSource(dbSource, data -> {
            result.removeSource(dbSource);
            if (shouldFetch(data)) {
                // 缓存数据不足/过期,发起网络请求
                fetchFromNetwork(dbSource);
            } else {
                // 直接使用缓存数据,发送成功状态
                result.addSource(dbSource, newData -> setValue(Resource.success(newData)));
            }
        });
    }

    private void fetchFromNetwork(final LiveData<ResultType> dbSource) {
        // 发起具体的网络请求(由子类实现)
        LiveData<ApiResponse<RequestType>> apiResponse = createCall();
        // 添加网络请求作为数据源
        result.addSource(apiResponse, response -> {
            result.removeSource(apiResponse);
            result.removeSource(dbSource);
            if (response.isSuccessful()) {
                // 网络请求成功:先存数据库,再更新UI
                appExecutors.diskIO().execute(() -> {
                    saveCallResult(processResponse(response));
                    // 切回主线程,读取更新后的缓存数据
                    appExecutors.mainThread().execute(() ->
                            result.addSource(loadFromDb(), newData -> setValue(Resource.success(newData)))
                    );
                });
            } else {
                // 网络请求失败:回调错误,同时保留缓存数据展示
                onFetchFailed();
                result.addSource(dbSource, newData -> setValue(Resource.error(response.errorMessage, newData)));
            }
        });
    }

    private void setValue(Resource<ResultType> newValue) {
        // 避免重复发送相同状态
        if (!Objects.equals(result.getValue(), newValue)) {
            result.setValue(newValue);
        }
    }

    // ---------------------- 子类需实现的抽象方法 ----------------------
    // 从本地数据库读取缓存数据
    protected abstract LiveData<ResultType> loadFromDb();
    // 判断是否需要发起网络请求(比如缓存为空、过期)
    protected abstract boolean shouldFetch(ResultType data);
    // 创建具体的网络请求(比如Retrofit Call转LiveData)
    protected abstract LiveData<ApiResponse<RequestType>> createCall();
    // 将网络返回的数据保存到本地数据库
    protected abstract void saveCallResult(RequestType item);
    // 预处理网络响应(比如从Response中提取实体类)
    protected abstract RequestType processResponse(ApiResponse<RequestType> response);
    // 网络请求失败的回调(可选实现)
    protected void onFetchFailed() {}

    // 对外暴露最终的数据流,供UI层观察
    public LiveData<Resource<ResultType>> asLiveData() {
        return result;
    }
}
关键逻辑拆解
  • MediatorLiveData的作用:它能同时观察多个LiveData数据源(本地缓存+网络请求),自动处理数据源的添加/移除,完美适配“先读缓存,再决定是否请求网络”的流程。
  • 线程调度:通过AppExecutors把数据库操作放到磁盘IO线程,UI更新放到主线程,彻底避免ANR问题。
  • 状态统一管理:用Resource封装了加载中、成功、失败三种状态,UI层只需观察asLiveData()返回的数据流,就能统一处理不同状态的UI展示。
典型使用场景

这个抽象类特别适合需要离线缓存支持的APP,比如新闻、电商、社交类应用:

  • 用户无网时,直接展示本地缓存内容
  • 用户有网时,自动同步最新数据并更新缓存
  • 所有网络请求的状态(加载、成功、失败)都能统一处理,不用每个请求都写一遍重复逻辑

内容的提问来源于stack exchange,提问作者flopshot

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 04:01:51