From 45e1e2eccd1950e08a91c23005ff45dc7aaf814d Mon Sep 17 00:00:00 2001 From: Craig Russell Date: Wed, 22 Apr 2020 10:13:00 +0100 Subject: [PATCH] Add global RxJava error handler to look for UndeliverableExceptions After updating the app dependencies to newest versions of all libraries, we noticed an increase in UndeliverablExceptions. This, it seems, is most likely caused by our sync job being interrupted, so we in turn dispose of the RxJava subscription. However in cases where the networking capabilities have just dropped (a potential cause of the sync job being stopped) we would then get a bunch of IO-related exceptions being called and since we'd already disposed, RxJava would have nowhere to deliver the exception to, hence it would throw an UndeliverableException. This UndeliverableException wouldn't be caught by the RxJava global error handler (as we don't set one) so it would default to the uncaught exception handler for the thread. This change catches and logs UndeliverableExceptions but doesn't let the app crash because of them. For all other exceptions, they continue to defer to the uncaught exception handler as before. --- .../duckduckgo/app/global/DuckDuckGoApplication.kt | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/duckduckgo/app/global/DuckDuckGoApplication.kt b/app/src/main/java/com/duckduckgo/app/global/DuckDuckGoApplication.kt index e7910a89e100..83097f16a618 100644 --- a/app/src/main/java/com/duckduckgo/app/global/DuckDuckGoApplication.kt +++ b/app/src/main/java/com/duckduckgo/app/global/DuckDuckGoApplication.kt @@ -16,11 +16,8 @@ package com.duckduckgo.app.global -import android.app.Activity import android.app.Application -import android.app.Service import android.os.Build -import androidx.fragment.app.Fragment import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.OnLifecycleEvent @@ -40,8 +37,8 @@ import com.duckduckgo.app.global.rating.AppEnjoymentLifecycleObserver import com.duckduckgo.app.global.shortcut.AppShortcutCreator import com.duckduckgo.app.httpsupgrade.HttpsUpgrader import com.duckduckgo.app.job.AppConfigurationSyncer -import com.duckduckgo.app.notification.NotificationRegistrar import com.duckduckgo.app.notification.AndroidNotificationScheduler +import com.duckduckgo.app.notification.NotificationRegistrar import com.duckduckgo.app.referral.AppInstallationReferrerStateListener import com.duckduckgo.app.settings.db.SettingsDataStore import com.duckduckgo.app.statistics.AtbInitializer @@ -59,6 +56,8 @@ import com.duckduckgo.app.usage.app.AppDaysUsedRecorder import dagger.android.AndroidInjector import dagger.android.DispatchingAndroidInjector import dagger.android.HasAndroidInjector +import io.reactivex.exceptions.UndeliverableException +import io.reactivex.plugins.RxJavaPlugins import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch @@ -196,6 +195,13 @@ open class DuckDuckGoApplication : HasAndroidInjector, Application(), LifecycleO private fun configureUncaughtExceptionHandler() { Thread.setDefaultUncaughtExceptionHandler(alertingUncaughtExceptionHandler) + RxJavaPlugins.setErrorHandler { throwable -> + if (throwable is UndeliverableException) { + Timber.w(throwable, "An exception happened inside RxJava code but no subscriber was still around to handle it") + } else { + alertingUncaughtExceptionHandler.uncaughtException(Thread.currentThread(), throwable) + } + } } private fun recordInstallationTimestamp() {