tl; dr:如何在使用RxJava2进行Retrofit2调用时捕获IOExceptions(和后代)?
我正在使用Retrofit2和RxJava2。我已经指定了一个onError处理程序。尽管如此,我的应用仍然会在IOException
s时崩溃 - 特别是SocketTimeoutException
。我在Github上提交了issue反对Retrofit,但我想知道我是在误用它还是遗漏了一些明显的东西。
以下是我创建Retrofit服务的方法:
OkHttpClient.Builder okClient = new OkHttpClient.Builder()
.addInterceptor(/* a variety of interceptors */)
.connectTimeout(10, SECONDS)
.writeTimeout(10, SECONDS)
.readTimeout(30, SECONDS) // first attempt to head off timeout exception
.build();
RxJava2CallAdapterFactory rxAdapter =
RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io());
Factory gsonFactory = GsonAdapterFactory.gsonConverterFactory();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(endpoint)
.client(okClient)
.addCallAdapterFactory(rxAdapter)
.addConverterFactory(gsonFactory)
.build();
return retrofit.create(Thing.class);
改造定义示例:
@GET("/thing")
Flowable<Response<Thing>> getThing();
使用改装服务的示例呼叫:
service.getThing()
.map(response -> thingFromResponse(thing)) // here I check for a successful response
.subscribe(thing -> doStuffWithThing(thing), // Consumer<Thing> onNext
throwable -> handleError("Error getting thing", throwable) // Consumer<Throwable> onError
);
我希望我的handleError()
函数能够处理所有错误,但是IOException的所有后代似乎都绕过了这个并且崩溃了。
RxJava2CallAdapterFactory
状态的javadoc:
响应包装体(例如,Observable)使用响应对象调用onNext以获取所有HTTP响应,并使用IOException调用onError以解决网络错误。
尽管如此,我看到这个堆栈跟踪崩溃:
Fatal Exception: java.net.SocketTimeoutException: timeout
at okhttp3.internal.http2.Http2Stream$StreamTimeout.newTimeoutException(Http2Stream.java:593)
at okhttp3.internal.http2.Http2Stream$StreamTimeout.exitAndThrowIfTimedOut(Http2Stream.java:601)
at okhttp3.internal.http2.Http2Stream.takeResponseHeaders(Http2Stream.java:146)
at okhttp3.internal.http2.Http2Codec.readResponseHeaders(Http2Codec.java:120)
at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:67)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:120)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at com.myapp.backend.retrofit.interceptors.SigningInterceptor.intercept(SigningInterceptor.java:32)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at com.myapp.backend.retrofit.interceptors.UserTokenInterceptor.intercept(UserTokenInterceptor.java:28)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at com.myapp.backend.retrofit.interceptors.UserAgentHeaderInterceptor.intercept(UserAgentHeaderInterceptor.java:24)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:212)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:179)
at okhttp3.RealCall.execute(RealCall.java:63)
at retrofit2.OkHttpCall.execute(OkHttpCall.java:174)
at com.jakewharton.retrofit2.adapter.rxjava2.CallObservable.subscribeActual(CallObservable.java:41)
at io.reactivex.Observable.subscribe(Observable.java:10514)
at io.reactivex.internal.operators.observable.ObservableSubscribeOn$1.run(ObservableSubscribeOn.java:39)
at io.reactivex.Scheduler$1.run(Scheduler.java:134)
at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:59)
at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:51)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:154)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:269)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
将rxjava2适配器从1.1.0(带com.jakewharton
包)更新到2.2.0后,我没有再次发现问题。要么我很幸运,要么修好它。
作为最后一点,我还将我的Retrofit服务从返回Flowable<Response<Thing>>
的实例更改为Flowable<Result<Thing>>
,以直接引用该调用导致的任何Throwable
。这可能有所帮助,也可能是多余的。