【问题标题】:Android: Uncatchable exceptions with Retrofit2 and KotlinAndroid:Retrofit2 和 Kotlin 无法捕获的异常
【发布时间】:2020-03-13 05:31:15
【问题描述】:

我刚刚遇到了一个关于最新 Retrofit2 和 Kotlin 的非常棘手的问题。

客户端想通过retrofit2连接到服务器。

        val client = OkHttpClient.Builder()
            .addInterceptor(object: Interceptor {
                override fun intercept(chain: Interceptor.Chain): Response {
                    try{
                        chain.proceed(chain.request());
                        return chain.proceed(chain.request());
                     }catch(e:Exception){
                        e.printStackTrace()
                      //called
                     }

                    return chain.proceed(chain.request());//leads to crash
                }
            })
            .readTimeout(10, TimeUnit.SECONDS).build()

改造服务是这样创建的:

        try {//try/catch here is useless -> not called
        retrofit = Retrofit.Builder()
                .baseUrl(Constants.SERVER_BASEURL + ":3000")
                .client(client)
                .addConverterFactory(GsonConverterFactory.create())
                .build()
        }catch (e:Exception){
            e.printStackTrace()
        }

任何函数都以协程方式调用,类似这样:

@POST("/users/me/logout")
suspend fun logout(@Header("Authorization") token: String): ResponseBody

这一切正常,但一旦您失去与服务器的连接,就无法捕捉到错误,这会自动导致应用崩溃。

对于离线服务器,异常如下所示:

java.net.ConnectException: Failed to connect to /XXXXXXX:3000
    at okhttp3.internal.connection.RealConnection.connectSocket(RealConnection.kt:270)
    at okhttp3.internal.connection.RealConnection.connect(RealConnection.kt:176)
    at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:236)
    at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:109)
    at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:77)
    at okhttp3.internal.connection.Transmitter.newExchange$okhttp(Transmitter.kt:162)
    at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:35)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:112)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:87)
    at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:82)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:112)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:87)
    at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:84)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:112)
    at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:71)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:112)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:87)
    at com.project.app.Helpers.MasterViewModel$getRetrofitClient$client$1.intercept(MasterViewModel.kt:118)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:112)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:87)
    at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.kt:215)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:112)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:87)
    at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.kt:184)
    at okhttp3.RealCall$AsyncCall.run(RealCall.kt:136)
    at java.util.concurrent.ThreadPoolExecutor.processTask(ThreadPoolExecutor.java:1187)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1152)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
    at java.lang.Thread.run(Thread.java:784)

我知道的:

  • 事件的拦截,在第一个代码块中显示,抛出异常

    chain.proceed(chain.request());

    由于“intercept”的返回值是“Response”,人们会想到只返回一个虚拟响应。但这也导致了新的问题,因为创建这样一个虚拟响应非常困难,而且您会丢失所有请求调用。最好的办法是暂停发出的请求,直到服务器重新连接。

  • 第二个代码块内的 try,catch 块实际上是无用的。它无法防止崩溃,因为崩溃发生在请求/响应管道内,可能发生在由改造启动以调用挂起函数的协程内。

  • 不管服务器是否超时或根本不存在,只要发生连接问题,就会抛出无法捕获的异常,导致应用崩溃

我猜,这是一个尚未解决的普遍问题。

有类似的问题,比如这个:

Retrofit and OkHttpClient, catch connection timeout in failure method

但该解决方案不能在这里工作,因为它不会阻止关键部分“chain.proceed(chain.request());”被叫

任何帮助,除了分叉建议,将不胜感激。

【问题讨论】:

    标签: android kotlin retrofit2


    【解决方案1】:

    我猜你不需要try/catch你的改造初始化(也不需要自定义OkhttpClient),而是每次调用服务。创建apiClient并复用调用服务:

    val apiClient = retroft.create(TourService::class.java)
    ...
    //calling API:
    try{
        val responseBody = apiClient.logout("some token")
    }catch(x: Exception){
        //catch app crash
        when(x){
            is ConnectException -> //TODO: handle connection error       
            is UnknownHostException ->  //TODO: handle no internet connection error
        }
    }
    

    【讨论】:

    • 确实,似乎我错过了协程中的一个点,我没有捕捉到异常。谢谢:)
    猜你喜欢
    • 2010-10-29
    • 1970-01-01
    • 2011-04-19
    • 1970-01-01
    • 2018-01-01
    • 2021-10-09
    • 1970-01-01
    • 2021-09-17
    相关资源
    最近更新 更多