Rx Java - Как заставить flatmap продолжаться, если одна из наблюдаемых вызывает ошибку? - PullRequest
0 голосов
/ 07 января 2020

Я создаю приложение, которое берет список приложений, установленных на устройстве, и проверяет наличие обновлений версий в магазине Google Play.

Это мой метод получения информации о приложении на основе имени пакета:

    public Observable<DetailsResponse> getUpdates(@NonNull List<ApplicationInfo> apps) {
        return Observable.fromIterable(apps)
            .flatMap(appInfo -> googlePlayApiService.getDetails(appInfo.packageName));
    }

Работает нормально, если пакет действительно находится в магазине Google Play, но возвращает retrofit2.adapter.rxjava2.HttpException: HTTP 404, если имя пакета не найдено (ie: загруженное приложение)

Это мой метод обработки наблюдаемых:

 updatesViewController.getUpdates(apps)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .as(AutoDispose.autoDisposable(ViewScopeProvider.from(this)))
            .subscribe(responseItem -> responseList.add(responseItem),
                    throwable -> responseList.add(null), //404's here, doesn't do the onComplete at all.
                    () -> { // onComplete
                        for (int i = 0; i < apps.size(); ++i) {
                          if (responseList.get(i) != null && apps.get(i).isLowerVersion(responseList.get(i)) {
                              doSomething();
                          }
                     });

Если все приложения находятся в PlayStore, это работает как задумано. Я хочу сделать так, чтобы, если одно или несколько приложений не были найдены в игровом магазине, они все равно могли делать doSomething () с найденными приложениями, игнорируя приложения, которых нет. Любая помощь приветствуется!

Ответы [ 4 ]

1 голос
/ 07 января 2020

Вы добавляете null (responseList.add(null)) в список ответов, когда вы нажимаете одну из этих 404 (приложений, не зарегистрированных в игровом магазине).

Тогда, как логично, вы что-то проверяете и делаете, если версия приложения ниже, так что вы можете doSomething(). Но проверка также проверяет нулевые значения (if (responseList.get(i) != null [...]), поэтому у вас есть нулевые значения в списке, и они ничего не сделают.

Зависит ли doSomething от каких-либо данных в элементе? Если нет, вы могли бы сделать что-то вроде:

if(responseList.get(i) == null || (responseList.get(i) != null && apps.get(i).isLowerVersion(responseList.get(i)))

Это вызовет doSomething для всех приложений с более низкой версией ИЛИ пропавших (например, те, которые привели к 404-м). Помните приведенное выше предположение, что doSomething ( ) не нужны фактические данные из этого поиска - в противном случае последующие действия в doSomething() завершатся неудачей.

0 голосов
/ 08 января 2020

Вы можете использовать onErrorResumeNext (..)

public Observable<DetailsResponse> getUpdates(@NonNull List<ApplicationInfo> apps) {
    return Observable.fromIterable(apps)
        .flatMap(appInfo -> googlePlayApiService.getDetails(appInfo.packageName).OnErrorResumeNext(getFallbackStream()));
}

Однако вы должны помнить, что HTTP 404 не является ошибкой, так как * Был сделан вызов 1008 *, если только ваш соединитель HTTP не сопоставил 404 с throwable на более низком уровне. Если нет, то вам придется сделать что-то вроде:

if(404 == appInfo.getResponseCode()) {
   return getFallbackStream();
}

внутри flatMap.

0 голосов
/ 07 января 2020

Мое решение этой проблемы:

Я добавил .onErrorResumeNext(Observable.empty());, который эффективно пропускает элементы, которые вызывают ошибки.

public Observable<DetailsResponse> getUpdates(@NonNull List<ApplicationInfo> apps) {
     return Observable.fromIterable(apps)
        .flatMap(appInfo -> googlePlayApiService.getDetails(appInfo.packageName)
        .onErrorResumeNext(Observable.empty()));
}

Затем в onComplete, вместо того, чтобы перебирать все мои приложения Я только l oop через те, которые находятся в responseList, который был в основном просто ошибка logi c, а не ошибка rx Java.

0 голосов
/ 07 января 2020

Вы можете использовать onErrorReturnItem(...) для возврата пустого результата при возникновении ошибки:

public Observable<DetailsResponse> getUpdates(@NonNull List<ApplicationInfo> apps) {
    return Observable.fromIterable(apps)
        .onErrorReturnItem(new ApplicationInfo())
        .flatMap(appInfo -> googlePlayApiService.getDetails(appInfo.packageName));
 }

В качестве альтернативы, вы также можете использовать onErrorResumeNext(...):

public Observable<DetailsResponse> getUpdates(@NonNull List<ApplicationInfo> apps) {
    return Observable.fromIterable(apps)
        .onErrorResumeNext(Observable.just(new ApplicationInfo()))
        .flatMap(appInfo -> googlePlayApiService.getDetails(appInfo.packageName));
 }

Обратите внимание, что вы может также выдать null при ошибке вместо пустого объекта, если вы хотите обработать его таким образом:

.onErrorReturnItem(null) или .onErrorResumeNext(Observable.just(null))

...