Вызов Retrofit2 с RxJava2 иногда вызывает IOException (вместо вызова обработчика onError)
tl;dr: Как мне перехватить IOExceptions (и потомков) при выполнении вызовов Retrofit2 с использованием RxJava2?
обсуждение
Я использую Retrofit2 с RxJava2. Я назначил обработчик onError. Тем не менее, мое приложение по-прежнему иногда зависает с IOException
с - в частности, SocketTimeoutException
, Я подал жалобу на Retrofit на Github, но мне интересно, не использую ли я ее или упускаю что-то очевидное.
Вот как я создаю свои сервисы 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, кажется, обходят это и вместо этого дают сбой.
Javadoc для RxJava2CallAdapterFactory
состояния:
Обернутое в ответ тело (например, Observable) вызывает onNext с объектом Response для всех HTTP-ответов и вызывает onError с IOException для сетевых ошибок.
Тем не менее, я вижу сбои с этой трассировкой стека:
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, я больше не наблюдал проблемы. Либо мне повезло, либо это исправило.
В заключение, я также изменил свои услуги по модернизации с возврата экземпляров Flowable<Response<Thing>>
в Flowable<Result<Thing>>
чтобы получить прямую ссылку на любой Throwable
S вызвано вызовом. Это могло бы помочь или было излишним.