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

社交类应用多数据源管理求助(Room+Remote+RxJava)

嘿,我来帮你解决这个多数据源的问题!你的需求其实就是典型的「先显示本地缓存,同时拉取远程更新」的社交APP模式,RxJava完全可以轻松搞定,咱们一步步来:

首先,修正Repository的返回类型

你现在的getMovies()返回List<Movie>是同步的,完全不符合RxJava异步流的设计,必须改成返回Observable<Resource<List<Movie>>>(Observable更适合处理多发射场景)。这里我们用一个Resource包装类来区分本地和远程数据,方便UI层精准处理。

编写Resource包装类(可选但推荐)

这个类帮我们标记数据来源,让UI明确知道什么时候显示本地数据,什么时候处理远程更新:

public class Resource<T> {
    public enum DataSource { LOCAL, REMOTE }
    public final DataSource source;
    public final T data;

    public Resource(DataSource source, T data) {
        this.source = source;
        this.data = data;
    }
}

实现Repository的核心逻辑

这里我们用Observable.merge()来并行发起本地和远程请求——本地数据因为是磁盘查询会先返回,远程请求在后台同时进行,完美契合你的需求:

public class MoviesRepository {
    private final RoomLocalDataSource roomLocalDataSource;
    private final RemoteDataSource remoteDataSource;

    // 构造函数注入数据源
    public MoviesRepository(RoomLocalDataSource roomLocalDataSource, RemoteDataSource remoteDataSource) {
        this.roomLocalDataSource = roomLocalDataSource;
        this.remoteDataSource = remoteDataSource;
    }

    public Observable<Resource<List<Movie>>> getMovies() {
        // 1. 本地数据源流:获取本地数据并标记为LOCAL
        Observable<Resource<List<Movie>>> localStream = roomLocalDataSource.getMoviesFromLocal()
                .toObservable()
                .map(localMovies -> new Resource<>(Resource.DataSource.LOCAL, localMovies))
                .onErrorReturn(throwable -> new Resource<>(Resource.DataSource.LOCAL, Collections.emptyList()));

        // 2. 远程数据源流:获取远程数据后先保存到本地,再标记为REMOTE
        Observable<Resource<List<Movie>>> remoteStream = remoteDataSource.getMoviesFromRemote()
                .toObservable()
                .doOnSuccess(remoteMovies -> roomLocalDataSource.saveMoviesToLocal(remoteMovies)) // 记得给RoomLocalDataSource添加保存方法
                .map(remoteMovies -> new Resource<>(Resource.DataSource.REMOTE, remoteMovies))
                .onErrorResumeNext(throwable -> Observable.empty()); // 远程失败不影响本地数据显示

        // 3. 合并两个流,并行执行
        return Observable.merge(localStream, remoteStream);
    }
}

ViewModel层如何处理

在ViewModel里订阅这个流,根据数据来源做不同处理:

public class MoviesViewModel extends ViewModel {
    private final MoviesRepository repository;
    public final MutableLiveData<List<Movie>> moviesLiveData = new MutableLiveData<>();
    public final MutableLiveData<Boolean> hasNewPosts = new MutableLiveData<>();

    public MoviesViewModel(MoviesRepository repository) {
        this.repository = repository;
        loadMovies();
    }

    private void loadMovies() {
        repository.getMovies()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(resource -> {
                    if (resource.source == Resource.DataSource.LOCAL) {
                        // 第一次收到本地数据,直接显示到RecyclerView
                        moviesLiveData.setValue(resource.data);
                        hasNewPosts.setValue(false);
                    } else if (resource.source == Resource.DataSource.REMOTE) {
                        // 远程数据拉取成功,显示「查看新帖子」按钮
                        hasNewPosts.setValue(true);
                    }
                }, throwable -> {
                    // 处理全局错误,比如Toast提示网络异常
                });
    }

    // 点击「查看新帖子」按钮时调用,刷新为最新本地数据(已包含远程拉取的内容)
    public void refreshMovies() {
        roomLocalDataSource.getMoviesFromLocal()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(moviesLiveData::setValue);
    }
}

为什么你之前用concat不行?

concat()是串行执行的:先完成本地请求,再发起远程请求,这会导致远程请求被延迟,完全不符合你「同时拉取远程」的需求。而merge()是并行执行两个流,本地数据先返回显示,远程请求在后台同步进行,正好匹配Instagram/LinkedIn的机制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 12:22:31