在拦截器中抛出 IOException 导致 Retrofit 崩溃

Throwing IOException in Interceptor is causing the crash with Retrofit

我正在尝试处理由于服务器和客户端网络连接引起的异常。 我制作了一个拦截器 class 用于在全局范围内处理这些错误作为方形文档。 每次我尝试在没有网络连接的情况下执行网络调用时,都会出现此错误并导致我的应用程序崩溃。

已编辑:如果连接速度太慢而无法触发 SocketTimeOutExeception ,它也会导致应用程序崩溃。我试图用一种只有在有活动的网络连接时才会执行网络调用的方式来覆盖它。但是就像我之前说的,连接速度太慢,错误与 SocketTimeOutExeception 一样。该应用程序在良好、活跃的网络连接下运行良好

W/System.err: java.net.UnknownHostException: Unable to resolve host "0725394a.ngrok.io": No address associated with hostname
        at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:157)
        at java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:105)
        at java.net.InetAddress.getAllByName(InetAddress.java:1154)
W/System.err:     at okhttp3.Dns$Companion$DnsSystem.lookup(Dns.kt:49)
        at okhttp3.internal.connection.RouteSelector.resetNextInetSocketAddress(RouteSelector.kt:164)
        at okhttp3.internal.connection.RouteSelector.nextProxy(RouteSelector.kt:129)
        at okhttp3.internal.connection.RouteSelector.next(RouteSelector.kt:71)
        at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:197)
        at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:107)
        at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:75)
        at okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.kt:245)
        at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:32)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
        at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:82)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
        at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
        at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:74)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
        at com.network.di.AuthenticationInterceptor.intercept(AuthenticationInterceptor.kt:63)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
        at com.network.exception.NetworkExceptionInterceptor.intercept(NetworkExceptionInterceptor.kt:11)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
        at com.readystatesoftware.chuck.ChuckInterceptor.intercept(ChuckInterceptor.java:172)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
        at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.kt:219)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
        at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:197)
        at okhttp3.internal.connection.RealCall.execute(RealCall.kt:148)
        at retrofit2.OkHttpCall.execute(OkHttpCall.java:188)
        at retrofit2.DefaultCallAdapterFactory$ExecutorCallbackCall.execute(DefaultCallAdapterFactory.java:97)
W/System.err:     at com.network.datasource.CourseDetailsDataSourcecImpl.getChaptersByPagination(CourseDetailsDataSourcecImpl.kt:39)
        at com.data.repository.CourseDetailsRepositoryImpl.getChapterList(CourseDetailsRepositoryImpl.kt:19)
        at com.domain.interactors.coursedetails.GetCourseChaptersByPagination$provideObservable.call(GetCourseChaptersByPagination.kt:26)
        at com.domain.interactors.coursedetails.GetCourseChaptersByPagination$provideObservable.call(GetCourseChaptersByPagination.kt:11)
        at io.reactivex.internal.operators.observable.ObservableFromCallable.subscribeActual(ObservableFromCallable.java:43)
        at io.reactivex.Observable.subscribe(Observable.java:12246)
        at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96)
        at io.reactivex.internal.schedulers.ExecutorScheduler$ExecutorWorker$BooleanRunnable.run(ExecutorScheduler.java:288)
        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:764)

这是我遇到的错误。

这是我的拦截器 classes

class AuthenticationInterceptor() : Interceptor {
    private var bearerToken : String? = ""
    private var token : String? = ""
    private var customToken : String? = null
    private var customTag : String? = null
    private var onlyUseCustomHeader : Boolean = false

    private var customName : String? = null


    fun setCustom(customName : String? , customTag : String? , customToken : String?) {
        this.customToken = customToken
        this.customTag = customTag
        this.customName = customName
    }


    fun setOnlyUseCustomHeader(onlyUseCustomHeader : Boolean) {
        this.onlyUseCustomHeader = onlyUseCustomHeader
    }


    constructor(
        bearerToken : String?,
        token : String?
    ) : this() {
        this.bearerToken = bearerToken
        this.token = token
    }

    @Throws(IOException::class)
    override fun intercept(chain : Interceptor.Chain) : Response {
        val builder = chain.request().newBuilder()
        if (!onlyUseCustomHeader) {
            if (customName != null && customToken != null) {
                if (customTag == null)
                    builder.addHeader("$customName" , "$customToken")
                else
                    builder.addHeader("$customName" , "$customTag $customToken")
            }

            if (bearerToken != null && bearerToken != "")
                builder.addHeader("Authorization" , "Bearer $bearerToken")
            if (token != null && token != "")
                builder.addHeader("token" , "$token")
        } else {
            if (customName != null && customToken != null) {
                if (customTag == null)
                    builder.addHeader("$customName" , "$customToken")
                else
                    builder.addHeader("$customName" , "$customTag $customToken")
            }
        }
        builder.addHeader("Accept" , "application/json")

        val request = builder.build()
        return chain.proceed(request)
    }

}

class NetworkExceptionInterceptor : Interceptor {
    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {

        val response = chain.proceed(chain.request())
        when (response.isSuccessful) {
            true  -> return response
            false -> {
                throw NetworkException(response.body , response.code)
            }
        }

    }
}

这是我的自定义错误 class。

data class NetworkException constructor(
    var errorBody: ResponseBody? = null,
    var errorCode: Int
) : IOException() 

这些是提供 OkhttpClient 和 Retrofit.Builder() 的方法,我在其中添加了网络拦截器和身份验证拦截器。

        @Provides
        fun providesOkHttpClientBuilder(context : Context) : OkHttpClient.Builder {

            return OkHttpClient.Builder().apply {

                val loggerInterceptor = HttpLoggingInterceptor().apply {
                    level = when (BuildConfig.DEBUG) {
                        true  -> HttpLoggingInterceptor.Level.HEADERS
                        false -> HttpLoggingInterceptor.Level.NONE
                    }
                }

                addInterceptor(loggerInterceptor)
                      .addInterceptor(ChuckInterceptor(context))
                    .addInterceptor(NetworkExceptionInterceptor())
                    .readTimeout(8 , TimeUnit.SECONDS)
                    .writeTimeout(8 , TimeUnit.SECONDS)
                    .connectTimeout(8 , TimeUnit.SECONDS)
                    .cache(null)
            }
        }

   @JvmStatic
        @NonNull
        @Named("authenticatedBuilder")
        @Provides
        fun getAuthenticatedBuilder(@Named("bearer_token") bearerToken : String? , @Named("token") token : String? , httpClientBuilder : OkHttpClient.Builder ,@Named("primary") retrofitBuilder : Retrofit.Builder) : Retrofit.Builder {
            showLogD("NETWORK_MODULE" , "BearerToken before interception : $bearerToken")
            showLogD("NETWORK_MODULE" , "TOKEN before interception : $token")
            val interceptor : Interceptor =
                AuthenticationInterceptor(bearerToken , token)
            if (!httpClientBuilder.interceptors().contains(interceptor)) {
                httpClientBuilder.addInterceptor(interceptor)
            }
            return retrofitBuilder.client(httpClientBuilder.build())

        }

已编辑 2:这是 CourseDetailsDataSourceImpl.kt

class CourseDetailsDataSourcecImpl @Inject constructor(
    private val courseDetailsService : CourseDetailsService ,
    private val courseDetailsMapper : CourseDetailsMapper ,
    private val chapterListMapper : CourseChapterListMapper ,
    private val lessonSnapShotMapper : LessonSnapShotMapper ,
    private val vdoPlayerOTPMapper : VdoPlayerOTPMapper
) : CourseDetailsDataSource {

    @Inject
    lateinit var vdoPlayerService : VdoPlayerService

    override fun getCourseDetails(courseID : String) : CourseDetailsVO {
        return courseDetailsMapper.map(courseDetailsService.getCourseDetails(courseID).execute().body()!!)
    }

    override fun getCourseVideoDetails(lessonID : String) : LessonSnapShotVO {
        return lessonSnapShotMapper.map(courseDetailsService.getLessonDetails(lessonID).execute().body()!!)
    }

    override fun getChaptersByPagination(courseID : String , pageNumber : Int) : CourseChapterListVO{
        //todo implement this with pagination
        return chapterListMapper.map(courseDetailsService.getCourseChapters(courseID).execute().body()!!)
    }

    override fun getVdoPlayerOtp(videoID : String) : VdoPlayerOTPResponseVO {
        return vdoPlayerOTPMapper.map(vdoPlayerService.getOtp( videoID , VDOOTPRequestBody()).execute().body()!!)
    }

}

知道如何解决这个问题吗?

我正在使用 onErrorReturn 方法,因为我正在使用 flatmap 调用链中的多个网络调用。这样做的副作用是 onErrorReturn 不会消耗网络调用由于某种故障而抛出的异常,因此它会抛出异常,因此应用程序不可避免地会崩溃。这就是我改用 onErrorResumeNext 的原因。然后崩溃就消失了,因为它吞下了异常,抛出的异常可以自己处理。