android - 在Interceptor中抛出IOException会导致Retrofit崩溃

标签 android kotlin retrofit2 interceptor ioexception

由于服务器和客户端网络连接,我试图处理异常。
我制作了一个Interceptor类,用于以正方形文档的形式全局处理这些错误。
每当我尝试在没有网络连接的情况下执行网络 call 时,都会出现此错误,并导致我的应用崩溃。

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

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$1.call(GetCourseChaptersByPagination.kt:26)
        at com.domain.interactors.coursedetails.GetCourseChaptersByPagination$provideObservable$1.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)

这是我一直得到的错误。

这是我的拦截器类
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)
            }
        }

    }
}

这是我的自定义错误类。
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的原因。然后崩溃消失了,因为它吞没了异常,并且抛出的异常可以由我们自己处理。

关于android - 在Interceptor中抛出IOException会导致Retrofit崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60278936/

相关文章:

java - 使用一种布局并在 Eclipse 中以编程方式更改字符串

HTTPS GET 调用适用于第 3 方 API 上的浏览​​器,但不适用于 curl/fuel

java - Retrofit2 和 converter-gson : Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $

Android Retrofit2 reddit 404 未找到

android - 什么时候应该在 Flutter 中使用 BLoC 模式?

android - 使用 Wea​​r OS Android 拍照

Android通用图像加载器,显示图像的顶部部分

Kotlin commonMain 与 java.io.Serializable

android - 如何避免在 Kotlin 中使用 lateinit 字段进行部分覆盖

android - Retrofit 2,解析json,忽略数组内部的错误数据类型