Android:Retrofit2 和 Kotlin 无法捕获的异常
Android: Uncatchable exceptions with Retrofit2 and Kotlin
我刚刚遇到一个关于最新的 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.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" 的 return 值是 "Response" 人们会想到只是 return 一个虚拟的响应。但这也导致了新问题,因为创建这样的虚拟响应非常困难,而且您会丢失所有请求调用。最好的办法是暂停发出的请求,直到服务器重新连接。
第二个代码块中的 try、catch 块几乎没有用。它对防止崩溃没有任何作用,因为崩溃发生在 request/response 管道内,可能是在由改造启动以调用挂起函数的协程内。
- 无论服务器是否超时或根本不存在都没有关系,在发生任何连接问题的那一刻,将抛出无法捕获的异常并导致应用程序崩溃
我猜,这是一个普遍的问题,还没有解决。
还有类似的问题,比如这个:
但是解决方案在这里不起作用,因为它不能阻止关键部分 "chain.proceed(chain.request());" 被调用
任何帮助,除了分叉建议,将不胜感激。
我想您不需要 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
}
}
我刚刚遇到一个关于最新的 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.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" 的 return 值是 "Response" 人们会想到只是 return 一个虚拟的响应。但这也导致了新问题,因为创建这样的虚拟响应非常困难,而且您会丢失所有请求调用。最好的办法是暂停发出的请求,直到服务器重新连接。
第二个代码块中的 try、catch 块几乎没有用。它对防止崩溃没有任何作用,因为崩溃发生在 request/response 管道内,可能是在由改造启动以调用挂起函数的协程内。
- 无论服务器是否超时或根本不存在都没有关系,在发生任何连接问题的那一刻,将抛出无法捕获的异常并导致应用程序崩溃
我猜,这是一个普遍的问题,还没有解决。
还有类似的问题,比如这个:
但是解决方案在这里不起作用,因为它不能阻止关键部分 "chain.proceed(chain.request());" 被调用
任何帮助,除了分叉建议,将不胜感激。
我想您不需要 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
}
}