如何使用 switchIfEmpty RxJava

7

这里的逻辑是,如果数据库中的评分为空,那么我希望从API获取它们。 我有以下代码:

Observable.from(settingsRatingRepository.getRatingsFromDB())
            .toList()
            .switchIfEmpty(settingsRatingRepository.getSettingsRatingModulesFromAPI())
            .compose(schedulerProvider.getSchedulers())
            .subscribe(ratingsList -> {
                view.loadRatingLevels(ratingsList, hideLocks);
            }, this::handleError);
getRatingsFromDB()调用返回List<SettingRating>,但API调用返回Observable<List<SettingRating>>
然而,在进行单元测试时,当我从数据库调用传递一个空列表时,它不会执行API调用。请有人帮我解决这个问题。这是我的单元测试代码:
when(mockSettingsRatingsRepository.getRatingsFromDB()).thenReturn(Collections.emptyList());
List<SettingsRating> settingsRatings = MockContentHelper.letRepositoryReturnSettingsRatingsFromApi(mockSettingsRatingsRepository);

settingsParentalPresenter.onViewLoad(false);

verify(mockView).loadRatingLevels(settingsRatings, false);
4个回答

10
正如@Kiskae所提到的那样,我混淆了空列表和空Observable之间的区别。因此,我使用了以下解决方案,这正是我想要的:
public void onViewLoad(boolean hideLocks) {
    Observable.just(settingsRatingRepository.getRatingsFromDB())
            .flatMap(settingsRatings -> {
                if (settingsRatings.isEmpty()) {
                    return settingsRatingRepository.getSettingsRatingModules();
                } else {
                    return Observable.just(settingsRatings);
                }
            })
            .compose(schedulerProvider.getSchedulers())
            .subscribe(ratingsList -> {
                view.loadRatingLevels(ratingsList, hideLocks);
            }, this::handleError);
}

4

Observable#toList() 返回一个单一元素。如果该observable为空,则它将发出一个空列表。因此,根据定义,在调用 toList()后observable永远不会为空。


啊,好的,如果我使用Observable.just()呢?getRatingsFromDB()方法返回一个List<SettingRating>,但API调用返回一个Observable<List<SettingRating>> - blackpanther
为了使其按照您的意愿运行,可以使用Observable.just()Observable.filter()来清空可观察对象(如果列表为空)。因此,代码如下:Observable.just(getRatingsFromDB()).filter(list -> !list.isEmpty()) - Kiskae

4

switchIfEmpty 只有在您的观察者在不发出任何项目的情况下完成时才会被调用。 由于您正在使用 toList,它将发出列表对象。这就是为什么您的 switchIfEmpty 没有被调用的原因。

如果您想从缓存中获取数据并在缓存为空时返回您的 API,请使用 concat 运算符和 firsttakeFirst 运算符。

例如:

Observable.concat(getDataFromCache(), getDataFromApi())
            .first(dataList -> !dataList.isEmpty());

0

在@kiskae的答案基础上,您使用的toList()将聚合的元素作为单个List发出。

这里有一种替代方法,可以避免使用Observable.just()+ flatMap

Observable.from将迭代您的服务返回的列表,并发出每个单独的项,因此是一个Observable<Rating>。如果该列表为空,则自然会产生一个空的Observable。您的API调用还会产生一个Observable<Rating>,在这两种情况下,您都希望重新聚合成一个List<Rating>以进行进一步处理。

因此,只需将toList()从原始代码中移动到switchIfEmpty调用API之后的下一行即可:

Observable.from(settingsRatingRepository.getRatingsFromDB())
    .switchIfEmpty(settingsRatingRepository.getSettingsRatingModulesFromAPI())
    .toList()
    .compose(schedulerProvider.getSchedulers())
    .subscribe(ratingsList -> {
        view.loadRatingLevels(ratingsList, hideLocks);
    }, this::handleError);

当然,这个解决方案可能会产生更多的垃圾(因为数据库的List被转换成一个Observable,然后再转换成一个新的List)。


我认为这不适合作为一个插拔式替换,因为如果我理解正确的话,settingsRatingRepository.getSettingsRatingModulesFromAPI() 返回的是 Observable<List<SettingsRating>> - Kiskae
啊,如果是这样的话,那我的错。 - Simon Baslé

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接