Как поймать UnknownHostException в одноразовой подписке - PullRequest
0 голосов
/ 01 апреля 2019

Мне нужно перехватить исключение UnknownHostException внутри метода Disposable .subscribe для одного конкретного сетевого вызова, используя RxJava2.

В одном из моих приложений для Android я получил очень случайную ошибку, на воспроизведение которой у меня ушло много времени. Я могу связать это с ненадежным подключением к сети / SIM-карте в Кейптауне, где мы развернули приложение для нескольких сотен пользователей. Ошибка не возникает с эмулятором или с любым телефоном, который я тестировал в Германии.

Все классические тестовые случаи, когда я переключаюсь с Wi-Fi на мобильные данные, выключаю мобильные данные / wifi или перехожу в режим полета, все эти случаи корректно обрабатываются приложением.

Приложение выполняет несколько повторяющихся запросов API и получает данные через FCM. Все они работают нормально. Но есть один запрос на отправку обновления статуса при открытии приложения. Этот запрос не выполняется, если временно возникла проблема с сетью. Я сейчас в Кейптауне для тестирования, и эти проблемы с сетью настолько короткие и частые, что Android не распознает их достаточно быстро для стандартных способов обработки недоступного соединения. Я вижу, что проблема возникает здесь один или два раза в неделю с несколькими сотнями телефонов, работающих 24/7.

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

    public void sendActiveGuardStatus() {
        GuardManager guardManager = GuardManager.newInstance(getBaseContext());
        showProgress("Loading Operation Status");
        Disposable subscription = guardManager
                .setGuardAvailable()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .timeout(8, TimeUnit.SECONDS)
                .onErrorReturn(e -> {return null;})
                .subscribe(
                        response -> {
//                            if (Math.random() < 0.5) {
//                                throw new UnknownHostException("Oh no, no network...");
//                            }
                            hideProgress();
                            startHomeActivity();
                        }
                        , throwable -> {
                            hideProgress();
                            startLoginActivity();
                        }
                );

        addSubscription(subscription);
    }

Вот как строится запрос:

public class GuardManager {

    public static GuardManager newInstance(Context context) {
        return new GuardManager(
                context,
                RetrofitFactory.build(context).create(GuardNetworkService.class),
                new GuardUtils()
        );
    }

...

    public Single<SetGuardStatusResponse> setGuardAvailable() {
        return networkService
                .setGuardStatus("available")
                .compose(response -> TokenErrorTransformer.withErrorCheck(response, context));
    }

...
}
  public interface GuardNetworkService {
  ...

    @PUT("guardian/set_status_{guard_status}")
    Single<SetGuardStatusResponse> setGuardStatus(@Path("guard_status") String status);

  ...
}

Я хочу поймать исключение, созданное внутри .subscribe (...), и обработать его снаружи вручную. В конце я хочу повторить попытку 3 раза с задержкой между запросами.

Я попробовал onError () и все комбинации try / catch, о которых только мог подумать, но я всегда получаю исключение, подобное этому:

2019-04-01 12:00:37.682 11683-11683/com.lionizers.guardapp W/System.err: io.reactivex.exceptions.UndeliverableException: java.net.UnknownHostException: Oh no, no network...
2019-04-01 12:00:37.682 11683-11683/com.lionizers.guardapp W/System.err:     at io.reactivex.plugins.RxJavaPlugins.onError(RxJavaPlugins.java:367)
2019-04-01 12:00:37.682 11683-11683/com.lionizers.guardapp W/System.err:     at io.reactivex.internal.observers.ConsumerSingleObserver.onSuccess(ConsumerSingleObserver.java:66)

Я знаю, что мог бы использовать RxJavaPlugins.setErrorHandler, но я не знаю, как я мог бы использовать этот, чтобы специально повторить этот единственный запрос. Все остальные запросы обрабатываются через задачи / потоки, поэтому мне не нужен универсальный обработчик ошибок для любой другой части кода.

  1. Я что-то упустил концептуально? Есть ли лучший способ продолжить?
  2. Как бы я поймал исключение, возникшее внутри этого конкретного метода подписки?
...