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

RxJava实现缓存过期逻辑:Realm取数/超时从Api拉取(新手求助)

嘿,刚接触RxJava的话,这种「缓存优先+过期刷新」的场景其实是RxJava最擅长处理的典型案例之一!我给你拆解清楚实现思路和具体代码,保证你能快速上手~

核心思路拆解

我们的目标是:

  1. 先从Realm里读取缓存数据,判断是否在5分钟有效期内
  2. 如果缓存有效,直接返回缓存数据给Presenter
  3. 如果缓存过期/不存在,就调用API拉取新数据,保存到Realm后再返回给Presenter

用RxJava的操作符可以把这些逻辑串联得非常流畅,核心是用concat+firstElement来优先取缓存,缓存无效时自动 fallback 到API请求。

具体代码实现

第一步:定义带缓存时间的Realm实体类

首先给你的数据模型加一个lastUpdated字段,用来记录最后一次从API拉取数据的时间:

public class YourDataModel extends RealmObject {
    @PrimaryKey
    private String uniqueId; // 必须有主键,方便后续更新缓存
    // 你的其他数据字段
    private String content;
    private Date lastUpdated; // 缓存时间戳

    // Getter & Setter 方法
    public Date getLastUpdated() { return lastUpdated; }
    public void setLastUpdated(Date lastUpdated) { this.lastUpdated = lastUpdated; }
}

第二步:实现数据仓库(Repository)核心逻辑

这部分是整个流程的核心,我们把缓存判断、API请求、Realm操作都封装在这里:

public class DataRepository {
    private final YourApiService apiService; // 你的API请求服务,比如Retrofit创建的实例

    public DataRepository(YourApiService apiService) {
        this.apiService = apiService;
    }

    // 判断缓存是否在5分钟有效期内
    private boolean isCacheValid(YourDataModel cachedData) {
        if (cachedData == null) return false;
        long currentTime = System.currentTimeMillis();
        long cacheTimestamp = cachedData.getLastUpdated().getTime();
        // 5分钟 = 5*60*1000毫秒
        return (currentTime - cacheTimestamp) < 300000;
    }

    // 对外暴露的获取数据方法,返回Observable
    public Observable<YourDataModel> getData() {
        // 1. 构建缓存Observable:从Realm读取并过滤有效缓存
        Observable<YourDataModel> cacheObservable = Observable.fromCallable(() -> {
            Realm realm = Realm.getDefaultInstance();
            try {
                // 读取缓存并拷贝到非Realm对象(避免跨线程问题)
                return realm.copyFromRealm(realm.where(YourDataModel.class).findFirst());
            } finally {
                realm.close(); // 务必关闭Realm实例
            }
        }).filter(this::isCacheValid); // 只保留有效缓存

        // 2. 构建API Observable:请求数据并保存到Realm
        Observable<YourDataModel> apiObservable = apiService.fetchDataFromApi() // 假设这个方法返回Observable<YourDataModel>
                .flatMap(apiData -> {
                    // 把API返回的数据保存到Realm
                    return Observable.fromCallable(() -> {
                        Realm realm = Realm.getDefaultInstance();
                        try {
                            realm.executeTransaction(r -> {
                                apiData.setLastUpdated(new Date()); // 设置当前时间为缓存时间
                                r.copyToRealmOrUpdate(apiData); // 保存或更新数据
                            });
                            return apiData;
                        } finally {
                            realm.close();
                        }
                    });
                });

        // 3. 串联逻辑:先试缓存,缓存无效就走API,取第一个有效结果
        return Observable.concat(cacheObservable, apiObservable)
                .firstElement() // 只要第一个有效的数据源
                .toObservable();
    }
}

第三步:Presenter层调用

在Presenter里调用Repository的方法,处理线程调度和结果回调:

public class YourPresenter {
    private final DataRepository repository;
    private final YourView view; // 你的View接口,用于更新UI

    public YourPresenter(DataRepository repository, YourView view) {
        this.repository = repository;
        this.view = view;
    }

    public void loadData() {
        repository.getData()
                .subscribeOn(Schedulers.io()) // 耗时操作(Realm/API)放到IO线程
                .observeOn(AndroidSchedulers.mainThread()) // 结果回调到主线程更新UI
                .subscribe(
                        data -> view.showSuccessData(data), // 成功拿到数据,更新UI
                        error -> view.showError(error.getMessage()) // 处理请求失败
                );
    }
}
关键要点提醒
  1. Realm线程安全:Realm实例不能跨线程使用,所以每次操作都要在当前线程创建并关闭,这里用Observable.fromCallable把同步的Realm操作包装成Observable,方便线程调度。
  2. 线程调度必加:一定要用subscribeOn把IO操作放到后台线程,observeOn切回主线程,否则会阻塞UI。
  3. 错误处理兜底:如果API请求失败,可以给apiObservable加onErrorResumeNext,比如返回过期的缓存作为兜底:
    apiObservable = apiService.fetchDataFromApi()
            .flatMap(...)
            .onErrorResumeNext(throwable -> {
                // 这里可以返回过期缓存
                return Observable.fromCallable(() -> {
                    Realm realm = Realm.getDefaultInstance();
                    try {
                        return realm.copyFromRealm(realm.where(YourDataModel.class).findFirst());
                    } finally {
                        realm.close();
                    }
                }).filter(Objects::nonNull); // 确保缓存不为空
            });
    
  4. 缓存时间计算:注意时间单位是毫秒,别把5分钟算成5*60=300(秒),要转成300000毫秒。

刚开始用RxJava可能会觉得操作符有点绕,但多写几次这种场景,很快就能掌握它的链式思维啦!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:24:30