How to use switchIfEmpty RxJava

杀马特。学长 韩版系。学妹 提交于 2020-01-03 11:28:49

问题


The logic here is that if the ratings in the database are empty, then I want to get them from the API. I have the following code:

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

The getRatingsFromDB() call returns List<SettingRating>, but the API call returns Observable<List<SettingRating>>.

However, when I unit test this, when I pass an empty list from the database call, it does not execute the API call. Can someone pls help me in this matter. This is my unit test code:

when(mockSettingsRatingsRepository.getRatingsFromDB()).thenReturn(Collections.emptyList());
List<SettingsRating> settingsRatings = MockContentHelper.letRepositoryReturnSettingsRatingsFromApi(mockSettingsRatingsRepository);

settingsParentalPresenter.onViewLoad(false);

verify(mockView).loadRatingLevels(settingsRatings, false);

回答1:


As @Kiskae mentioned, it's the fact that I am confusing an empty list with an empty Observable. Therefore, I have used the following which is what I want:

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



回答2:


switchIfEmpty will only be called when your observer completes without emitting any items. Since you are doing toList it will emit list object. Thats why your switchIfEmpty is never getting called.

If you want to get data from cache and fallback to your api if cache is empty, use concate along with first or takeFirst operator.

For example:

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



回答3:


Observable#toList() returns a single element. If the observable from which it gets its elements is empty, it will emit an empty list. So by definition the observable will never be empty after calling toList().




回答4:


Building on answer by @kiskae, your use of a toList() emits the elements aggregated as a single List.

There is an alternative to the use of Observable.just() + a flatMap here.

Observable.from will iterate over the list returned by your service and emit each individual items, so an Observable<Rating>. If said list is empty, it naturally produces an empty Observable. Your API call also produces an Observable<Rating>, and in both cases you want to reaggregate that back into a List<Rating> for further processing.

So just move the toList() from your original code down one line, after the switchIfEmpty calling the API:

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

Granted, that solution may produce a bit more garbage (as the db's List is turned into an Observable just to be turned into a new List later on).



来源:https://stackoverflow.com/questions/39078029/how-to-use-switchifempty-rxjava

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!