Используйте обработчик ошибок RxJavaPlugins вместо реализации onError

При работе с RxJava вы рано или поздно столкнетесь с этим исключением:

io.reactivex.exceptions.OnErrorNotImplementedException: The exception was not handled due to missing onError handler in the subscribe() method call.

Обработка ошибок имеет значение. RxJava выдает вам исключение, потому что вы не смогли решить проблему. Вы должны обработать ошибку, иначе ваше приложение выйдет из строя.

Будь то Observable, Single, Compleatable или даже Maybe, каждый поток RxJava предоставляет вам onError обратный вызов для обработки таких случаев. Вы должны реализовать этот обратный вызов везде.

Однако нельзя относиться ко всем ошибкам одинаково. Многие из них не нуждаются в особом лечении. Причина в том, что вы не можете ожидать, где может произойти исключение. Ошибки могут исходить из любого места вашего потока. Например, какой-то фрагмент кода может остановить поток в любое время, что приведет к ошибке CancellationException.

Итак, что делать в вашем обратном вызове onError? Возможно, вы могли бы добавить журнал, чтобы отслеживать проблему.

По крайней мере, вы гарантируете, что ваше приложение не выйдет из строя. Это защитный кодекс.

Потерпите меня; Я покажу вам еще один способ обработки ошибок, когда вам это нужно, без риска сбоя приложения.

Базовый вариант использования

Взгляните на поток опроса ниже:

Каждую секунду мы обновляем наш обзор. Обратите внимание, я не обработал ошибку. Ну что могло пойти не так? Это не значит, что метод interval из RxJava выйдет из строя!

Теперь давайте выделим механизм опроса в другой класс. Я назвал это PollableViewModel. PollableView не должны знать, как модель представления обрабатывает опрос. Только его контракт: он опрашивает каждую секунду.

По какой-то глупой причине startPolling восходящий поток оперирует результатом интервала. Пытаясь разделить его на ноль, поток выдает ArithmeticException.

В нисходящем потоке будет выдана ошибка, когда PollableView вызовет poll(). Поскольку мы не реализовали обратный вызов onError, мы получаем пресловутый OnErrorNotImplementedException.

Обработка ошибок по умолчанию

В этом случае я принудительно создал исключение, используя тупой код. Тем не менее, исключение может быть создано по причине, не зависящей от вас.

Вы всегда можете решить реализовать обратный вызов onError.

Здесь мы регистрируем исключение с Timber. Мы каким-то образом обрабатываем исключение, перенаправляя его в консоль журнала.

Я вижу два существенных недостатка:

  1. Реализовать обратный вызов без реальной обработки ошибок утомительно. Вы можете записать ошибку или оставить блок пустым. Это не принесет вам ничего, связанного с этой конкретной ошибкой.
  2. У вас нет механизма, гарантирующего, что вы обработали все ваши обратные вызовы. Это означает, что он все еще может где-то разбиться. И давайте посмотрим правде в глаза, вы кое-что забудете. Чтобы ответить на этот вопрос, вы можете использовать правила lint, такие как RxLint. Но опять же, никакой обработки ошибок не выполняется.

Или вы можете использовать обработчик ошибок RxJavaPlugins. Он получит все исключения везде, где отсутствуют onError реализации. Вы можете указать, что делать в таких случаях.

Добавьте этот код в свой onCreate пользовательский Application класс:

RxJavaPlugins.setErrorHandler(Timber::e)

Мы можем решить, как указано выше, регистрировать все неперехваченные исключения. Вы можете удалить все реализации onError, которые также не обрабатывают ошибки. Обработчик ошибок RxJavaPlugins поможет вам. Больше никаких OnErrorNotImplementedException сбоев!

Улучшение трассировки стека

Используя обработку ошибок по умолчанию, некоторые могут указать на потерю некоторой информации из трассировки стека.

Поскольку трассировка стека RxJava в любом случае не выделяется своей ясностью, я бы рекомендовал объединить обработчик ошибок по умолчанию с RxDogTag. Вы получите журналы, предназначенные для ошибочного потока. Большой подспорье при отладке!

RxDogTag.install()
RxJavaPlugins.setErrorHandler(Timber::e)

Обрабатывайте ошибки там, где это важно

Добавление обработки ошибок по умолчанию может показаться подверженным ошибкам. Вы правы, если скажете, что мы скрываем потенциальные проблемы.

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

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

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