RxJava:尝试将错误传播到Observer.onError时发生错误。

41

我在Rx Library中遇到了IllegalStateException错误,不确定问题的根源是在RxJava还是我可能正在做某些不正确的事情。

当进行证书固定(在所有服务器请求上都会发生)时,致命崩溃似乎指向会话超时或注销然后重新登录。复现步骤(大约25%的时间会发生)如下:登录,打开列表项 - 滚动到最后 - 注销 - 登出并重新登录 - 打开应用程序 - 关闭应用程序 -> 崩溃!

有人有任何想法如何防止这种情况吗?我在这里找到了类似的问题 Observer.onError firing off inconsistently

java.lang.IllegalStateException: Fatal Exception thrown on Scheduler.Worker thread.
   at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:62)
   at android.os.Handler.handleCallback(Handler.java:615)
   at android.os.Handler.dispatchMessage(Handler.java:92)
   at android.os.Looper.loop(Looper.java:137)
   at android.app.ActivityThread.main(ActivityThread.java:4867)
   at java.lang.reflect.Method.invokeNative(Method.java)
   at java.lang.reflect.Method.invoke(Method.java:511)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1007)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:774)
   at dalvik.system.NativeStart.main(NativeStart.java)
Caused by: rx.exceptions.OnErrorFailedException: Error occurred when trying to propagate error to Observer.onError
   at rx.observers.SafeSubscriber._onError(SafeSubscriber.java:201)
   at rx.observers.SafeSubscriber.onError(SafeSubscriber.java:111)
   at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber$2.call(OperatorObserveOn.java:159)
   at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
   at android.os.Handler.handleCallback(Handler.java:615)
   at android.os.Handler.dispatchMessage(Handler.java:92)
   at android.os.Looper.loop(Looper.java:137)
   at android.app.ActivityThread.main(ActivityThread.java:4867)
   at java.lang.reflect.Method.invokeNative(Method.java)
   at java.lang.reflect.Method.invoke(Method.java:511)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1007)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:774)
   at dalvik.system.NativeStart.main(NativeStart.java)
Caused by: rx.exceptions.CompositeException: 2 exceptions occurred. 
   at rx.observers.SafeSubscriber._onError(SafeSubscriber.java:201)
   at rx.observers.SafeSubscriber.onError(SafeSubscriber.java:111)
   at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber$2.call(OperatorObserveOn.java:159)
   at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
   at android.os.Handler.handleCallback(Handler.java:615)
   at android.os.Handler.dispatchMessage(Handler.java:92)
   at android.os.Looper.loop(Looper.java:137)
   at android.app.ActivityThread.main(ActivityThread.java:4867)
   at java.lang.reflect.Method.invokeNative(Method.java)
   at java.lang.reflect.Method.invoke(Method.java:511)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1007)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:774)
   at dalvik.system.NativeStart.main(NativeStart.java)
Caused by: rx.exceptions.CompositeException$CompositeExceptionCausalChain: Chain of Causes for CompositeException In Order Received =>
   at com.crashlytics.android.SessionDataWriter.getEventAppExecutionExceptionSize(SessionDataWriter.java:597)
   at com.crashlytics.android.SessionDataWriter.getEventAppExecutionExceptionSize(SessionDataWriter.java:600)
   at com.crashlytics.android.SessionDataWriter.getEventAppExecutionExceptionSize(SessionDataWriter.java:600)
   at com.crashlytics.android.SessionDataWriter.getEventAppExecutionSize(SessionDataWriter.java:533)
   at com.crashlytics.android.SessionDataWriter.getEventAppSize(SessionDataWriter.java:492)
   at com.crashlytics.android.CrashlyticsUncaughtExceptionHandler.writeSessionEvent(CrashlyticsUncaughtExceptionHandler.java:956)
   at com.crashlytics.android.CrashlyticsUncaughtExceptionHandler.access$200(CrashlyticsUncaughtExceptionHandler.java:56)
   at com.crashlytics.android.CrashlyticsUncaughtExceptionHandler$7.call(CrashlyticsUncaughtExceptionHandler.java:274)
   at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
   at java.util.concurrent.FutureTask.run(FutureTask.java:137)
   at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
   at io.fabric.sdk.android.services.common.ExecutorUtils$1$1.onRun(ExecutorUtils.java:58)
   at io.fabric.sdk.android.services.common.BackgroundPriorityRunnable.run(BackgroundPriorityRunnable.java:13)
   at java.lang.Thread.run(Thread.java:856)

嗨,Scott。请问您的Android版本、RxJava版本和RxAndroid版本是否可知?您是否正确处理了所有的订阅? - Ivan Morgillo
io.reactivex:rxandroid:0.24.0 io.reactivex:rxjava:1.0.12 Android 版本 5.1 是的,我正在取消所有订阅,然后将它们设置为 null。 - Scott B
你能试试 RxAndroid 1.0.1 https://github.com/ReactiveX/RxAndroid/releases/tag/v1.0.1 吗? - Ivan Morgillo
谢谢 @IvanMorgillo 我会试一下看看这个问题是否还在。谢谢。 - Scott B
4个回答

52

发生的情况是:您在 Subscriber 中的 onError 实现抛出了未经检查的异常,这违反了 Observable 的约定,并导致在 observeOn 调度程序中抛出 OnErrorFailedException 以中止可观察处理。


值得指出的是,如果您尝试通过在onError实现中记录日志来调试此问题,则可能无法在logcat中看到它。最好使用断点进行调试。这就是最初让我看不到问题的原因。 - tir38
我该如何跟踪onError()回调中的确切错误,而不是接收这个通用的堆栈跟踪? - Roman Nazarevych
如何修复这个问题? - Grzegorz Brzęczyszczykiewicz
1
如何修复?在您的onError实现中收到Throwable,然后在处理该错误时导致另一个错误。我会修复您的实现,使其不会这样做。为了帮助您调试,您可以通过try catch包装您的onError实现来记录抛出的错误。 - Dave Moten

14
你可能正在从你的onError回调中传递一个Activity上下文。当我试图显示一个AlertDialog时遇到了这个问题 - 传递给它特定的Activity上下文 - 然后在对话框弹出之前按下了返回按钮。我的建议是不要以这种方式传递Activity上下文。

1
这听起来更像是一个非常特定的情况。 - carlosavoy
@CarloLópezScutaro 你遇到了什么问题? - IgorGanapolsky
这种情况也发生在我身上了。你是怎么创建对话框的?我的对话框创建已经抽象成一个工具类,所以我需要传递一个Context来创建AlertDialog。 - Brandon
好的,但如果我们需要在调用onError时创建对话框呢? - Choletski
1
@Choletski 尝试使用 if(activity != null && isAdded()){ } 包装您的代码,如果您从片段中显示对话框。这应该可以防止此情况下的 IllegalStateException。 - lordmegamax

2
这是我的解决方法:
public abstract class MyNetworkSubscriber<T> extends Subscriber<T> {

@Override
public void onCompleted() {}

@Override
public void onError(Throwable e) {
    if (e instanceof HttpException) {
        ResponseBody responseBody = ((HttpException) e).response().errorBody();
        try {
            if (responseBody != null) {
                MyError error = new Gson().fromJson(responseBody.string(), MyError.class);
                onErrorCode(error);
            }
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    } else {
        e.printStackTrace();
    }
}

public void onErrorCode(MyError error){};
}

由于某些原因,OkHttp强制我捕获onError异常,否则OkHttp/Retrofit会崩溃并关闭应用。提供此解决方案后,
  1. 您可以选择覆盖您自己的onErrorCode,并获取来自API的更详细的错误对象,但您不必这样做。
  2. 已经覆盖了OnError,不再出现奇怪的崩溃。
  3. 最后但并非最不重要的是,由于<T>,它仍然是通用的!
您还可以强制覆盖onComplete和onError以使方法抽象化。

0

如果你想在 onError 中更新 UI,只需尝试:

api.subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Subscriber(){....})

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接