根据 Firebase Crashlytics,我的一些用户遇到了崩溃:
Fatal Exception: io.reactivex.rxjava3.exceptions.UndeliverableException: The exception could not be delivered to the consumer because it has already canceled/disposed the flow or the exception has nowhere to go to begin with. Further reading: https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling | java.lang.InterruptedException
at io.reactivex.rxjava3.plugins.RxJavaPlugins.onError(RxJavaPlugins.java:2)
at io.reactivex.rxjava3.internal.operators.single.SingleFromCallable.subscribeActual(SingleFromCallable.java:54)
at io.reactivex.rxjava3.core.Single.subscribe(Single.java:8)
at io.reactivex.rxjava3.internal.operators.single.SingleSubscribeOn$SubscribeOnObserver.run(SingleSubscribeOn.java:2)
at io.reactivex.rxjava3.core.Scheduler$DisposeTask.run(Scheduler.java:9)
at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:13)
at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
Caused by java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1024)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1334)
at java.util.concurrent.CountDownLatch.await(CountDownLatch.java:232)
at com.google.android.gms.tasks.zzaa.zza(zzaa.java:2)
at com.google.android.gms.tasks.Tasks.await(Tasks.java:28)
at com.firebase.ui.firestore.paging.FirestorePagingSource.lambda$loadSingle$0(FirestorePagingSource.java)
at com.firebase.ui.firestore.paging.FirestorePagingSource.$r8$lambda$aRsAFsiduxRWbX-Rs8tsd39JsV0(FirestorePagingSource.java)
at com.firebase.ui.firestore.paging.FirestorePagingSource$$InternalSyntheticLambda$0$301762c7c2c4bb9cc6676b1600ffb92473af44448cde26854aae0b3e15f6f4f6$0.call$bridge(FirestorePagingSource.java:92)
at io.reactivex.rxjava3.internal.operators.single.SingleFromCallable.subscribeActual(SingleFromCallable.java:16)
at io.reactivex.rxjava3.core.Single.subscribe(Single.java:8)
at io.reactivex.rxjava3.internal.operators.single.SingleSubscribeOn$SubscribeOnObserver.run(SingleSubscribeOn.java:2)
at io.reactivex.rxjava3.core.Scheduler$DisposeTask.run(Scheduler.java:9)
at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:13)
at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
我无法重现该错误。但是,在错误消息中链接的进一步阅读中,我可以阅读以下内容,这似乎与我的问题相关:
此外,一些第三方库/代码在获取时会抛出异常 被取消/处置调用中断,导致无法送达 大多数时候都是例外。 2.0.6 中的内部变化现在一致 在此之前取消或处置订阅/可处置 取消/处置任务或工作人员(这会导致中断 目标线程)。
// in some library try { doSomethingBlockingly() } catch (InterruptedException ex) { // check if the interrupt is due to cancellation // if so, no need to signal the InterruptedException if (!disposable.isDisposed()) { observer.onError(ex); } }
如果库/代码已经这样做了,则无法交付 InterruptedExceptions 现在应该停止。如果这个模式不是 以前使用过,我们鼓励更新有问题的代码/库。
下面是我使用 FirebaseUI-Android 设置 FirestorePagingAdapter 的代码。如前所述,它非常适合除少数用户之外的所有用户。
com.google.firebase.firestore.Query feedBaseQuery = firebaseHelper.getFeedBaseQuery();
PagingConfig config = new PagingConfig(10, 5, false);
FirestorePagingOptions<FeedObject> options = new FirestorePagingOptions.Builder<FeedObject>()
.setLifecycleOwner(mainActivity)
.setQuery(feedBaseQuery, config, FeedObject.class)
.build();
this.feedAdapter = new FirestoreFeedAdapter(mainActivity, options);
recyclerView.setHasFixedSize(false);
recyclerView.setLayoutManager(new LinearLayoutManager(mainActivity));
recyclerView.setAdapter(this.feedAdapter);
所以,我现在最好的猜测是应用程序在从 Firestore 检索数据时以某种方式被中断,这会引发从未捕获的 InterruptedException。我想我的问题是我是否应该以及在哪里捕获 InterruptedException?
当我在非常糟糕的网络或离线模式下快速更改片段时,我遇到了同样的错误,不太好,但我将其添加到我的应用程序类中。而且效果很好...
RxJavaPlugins.setErrorHandler { e ->
if (e is UndeliverableException) {
Log.d(TAG, e.toString())
} else {
Thread.currentThread().also { thread ->
thread.uncaughtExceptionHandler.uncaughtException(thread, e)
}
}
}
根据RxJava文档(https://github.com/ReactiveX/RxJava/wiki/What's- Different-in-2.0#error-handling),处理RxJava异常的推荐方法如下:
在应用程序的
handleRxJava3Error()
方法中实现 onCreate
方法。
确保导入必要的依赖项。
注意以下两个导入。
// For RxJava3
import io.reactivex.rxjava3.exceptions.UndeliverableException
import io.reactivex.rxjava3.plugins.RxJavaPlugins
// For RxJava2
import io.reactivex.exceptions.UndeliverableException
import io.reactivex.plugins.RxJavaPlugins
RxJava2 和 RxJava3 的 errorHandler 机制是相同的。但进口的就不一样了。不知道为什么他们会发展成这样。
import android.content.Context
import io.reactivex.rxjava3.exceptions.UndeliverableException
import io.reactivex.rxjava3.plugins.RxJavaPlugins
import java.io.IOException
import java.net.SocketException
fun Context.handleRxJava3Error() {
RxJavaPlugins.setErrorHandler { error ->
var e = error
if (e is UndeliverableException) {
e = e.cause ?:let { e }
}
if (e is IOException || e is SocketException) {
// Fine, irrelevant network problem or API that throws on cancellation
return@setErrorHandler
}
if (e is InterruptedException) {
// Fine, some blocking code was interrupted by a dispose call
return@setErrorHandler
}
if (e is NullPointerException || e is IllegalArgumentException) {
// This likely a bug in the application
Thread.currentThread().uncaughtExceptionHandler
?.uncaughtException(Thread.currentThread(), e)
?: logWarn(e.message)
return@setErrorHandler
}
if (e is IllegalStateException) {
// This is a bug in RxJava or in a custom operator
Thread.currentThread().uncaughtExceptionHandler
?.uncaughtException(Thread.currentThread(), e)
?: logWarn(e.message)
return@setErrorHandler
}
logWarn(e.message)
}
}