如何在 Android 的 OkHttp 拦截器中处理 IOExceptions?

Posted

技术标签:

【中文标题】如何在 Android 的 OkHttp 拦截器中处理 IOExceptions?【英文标题】:How to handle IOExceptions in OkHttp Interceptor in Android? 【发布时间】:2021-08-14 12:14:32 【问题描述】:

我有一个自定义拦截器来将 API 密钥附加到 API 请求。代码如下:

public class HeaderInterceptor implements Interceptor 

  private final ProfileDbDataSource profileDbRepository;

  @Inject public HeaderInterceptor(ProfileDbDataSource profileDbRepository) 
    this.profileDbRepository = profileDbRepository;
  

  @NonNull @Override public Response intercept(@NonNull Chain chain) throws IOException 
    if (!chain.request().url().toString().contains(Constants.GET_API_KEY)) 
      Request.Builder requestBuilder = attachApiKeyAsQueryParam(chain);
      return chain.proceed(requestBuilder.build());
    
    return chain.proceed(chain.request());
  

  private Request.Builder attachApiKeyAsQueryParam(Chain chain) 
    String apiKey = profileDbRepository.getLoggedInProfile().blockingGet().getApiKey();
    Request original = chain.request();
    HttpUrl originalHttpUrl = original.url();
    HttpUrl url = originalHttpUrl.newBuilder()
      .addQueryParameter(Constants.USER_API_KEY, apiKey)
      .build();
    return original.newBuilder().url(url);
  

我看到return chain.proceed(requestBuilder.build()); 行发生了一些崩溃,所有这些异常都是IOException 的子类。

我见过SocketTimeoutExceptionUnknownHostExceptionConnectException

这是一个堆栈跟踪:

io.reactivex.exceptions.UndeliverableException: 
  at io.reactivex.plugins.RxJavaPlugins.onError (RxJavaPlugins.java:367)
  at io.reactivex.internal.operators.observable.ObservableFlatMapSingle$FlatMapSingleObserver.innerError (ObservableFlatMapSingle.java:204)
  at io.reactivex.internal.operators.observable.ObservableFlatMapSingle$FlatMapSingleObserver$InnerObserver.onError (ObservableFlatMapSingle.java:289)
  at io.reactivex.internal.operators.single.SingleFlatMap$SingleFlatMapCallback.onError (SingleFlatMap.java:90)
  at io.reactivex.internal.operators.single.SingleSubscribeOn$SubscribeOnObserver.onError (SingleSubscribeOn.java:73)
  at io.reactivex.internal.operators.observable.ObservableSingleSingle$SingleElementObserver.onError (ObservableSingleSingle.java:93)
  at retrofit2.adapter.rxjava2.BodyObservable$BodyObserver.onError (BodyObservable.java:72)
  at retrofit2.adapter.rxjava2.CallExecuteObservable.subscribeActual (CallExecuteObservable.java:59)
  at io.reactivex.Observable.subscribe (Observable.java:12284)
  at retrofit2.adapter.rxjava2.BodyObservable.subscribeActual (BodyObservable.java:34)
  at io.reactivex.Observable.subscribe (Observable.java:12284)
  at io.reactivex.internal.operators.observable.ObservableSingleSingle.subscribeActual (ObservableSingleSingle.java:35)
  at io.reactivex.Single.subscribe (Single.java:3666)
  at io.reactivex.internal.operators.single.SingleSubscribeOn$SubscribeOnObserver.run (SingleSubscribeOn.java:89)
  at io.reactivex.Scheduler$DisposeTask.run (Scheduler.java:608)
  at io.reactivex.internal.schedulers.ScheduledRunnable.run (ScheduledRunnable.java:66)
  at io.reactivex.internal.schedulers.ScheduledRunnable.call (ScheduledRunnable.java:57)
  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.net.SocketTimeoutException: 
  at okhttp3.internal.http2.Http2Stream$StreamTimeout.newTimeoutException (Http2Stream.kt:677)
  at okhttp3.internal.http2.Http2Stream$StreamTimeout.exitAndThrowIfTimedOut (Http2Stream.kt:686)
  at okhttp3.internal.http2.Http2Stream.takeHeaders (Http2Stream.kt:143)
  at okhttp3.internal.http2.Http2ExchangeCodec.readResponseHeaders (Http2ExchangeCodec.kt:96)
  at okhttp3.internal.connection.Exchange.readResponseHeaders (Exchange.kt:106)
  at okhttp3.internal.http.CallServerInterceptor.intercept (CallServerInterceptor.kt:79)
  at okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.kt:109)
  at okhttp3.logging.HttpLoggingInterceptor.intercept (HttpLoggingInterceptor.kt:221)
  at okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.kt:109)
  at okhttp3.internal.connection.ConnectInterceptor.intercept (ConnectInterceptor.kt:34)
  at okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.kt:109)
  at okhttp3.internal.cache.CacheInterceptor.intercept (CacheInterceptor.kt:95)
  at okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.kt:109)
  at okhttp3.internal.http.BridgeInterceptor.intercept (BridgeInterceptor.kt:83)
  at okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.kt:109)
  at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept (RetryAndFollowUpInterceptor.kt:76)
  at okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.kt:109)
  at com.anstar.data.core.HeaderInterceptor.intercept (HeaderInterceptor.java:24)
  at okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.kt:109)
  at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp (RealCall.kt:201)
  at okhttp3.internal.connection.RealCall.execute (RealCall.kt:154)
  at retrofit2.OkHttpCall.execute (OkHttpCall.java:204)
  at retrofit2.adapter.rxjava2.CallExecuteObservable.subscribeActual (CallExecuteObservable.java:45)

【问题讨论】:

【参考方案1】:

这不是您的拦截器的问题,而是您如何使用网络请求的 Rx 流的问题。 IOExceptions 通常是预期的错误,您应该准备好在执行 I/O 时捕获和处理它们。 要使用 Rx Observable 执行此操作,请在调用 subscribe 时为 onError 参数传递错误处理程序:

apiClient.request()
    .subscribe(
        /* onNext */ (response) -> /* handle response */,
        /* onError */ (error) -> /* handle error */
    )

【讨论】:

是的,看来你是对的,但奇怪的是我没有任何没有 onError() 的 RxJava 链,因为我有 RxLint。我有一些 Observable.merges 和 .zip()。会不会是这个原因?你有什么想法吗? 那么我不确定...您是否可能在某处的 onError 回调中重新抛出异常或使用 Exceptions.propagate() ?在这种情况下,merge() 和 zip() 不应该相关。如果您找不到任何东西,我会尝试找出您的哪些网络调用确实导致了崩溃,并检查它们是否有问题。看看这个初学者:rongi.github.io/kotlin-blog/rxjava/2017/09/25/…

以上是关于如何在 Android 的 OkHttp 拦截器中处理 IOExceptions?的主要内容,如果未能解决你的问题,请参考以下文章

使用OkHttp拦截器和Retrofit进行缓存

Android开发老生新谈:从OkHttp原理看网络请求

Android 进阶之探索 OkHttp 原理

Android OKHttp 可能你从来没用过的拦截器 实用推荐

Android OkHttp3 :最简单&粗暴(使用与原理)讲解

Android网络请求框架—OKHttp 源码解析