首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Kotlin Android Retrofit 2.6.0具有协程错误处理功能

Kotlin Android Retrofit 2.6.0具有协程错误处理功能
EN

Stack Overflow用户
提问于 2019-08-02 16:58:25
回答 7查看 15.8K关注 0票数 25

我正在使用带有协程的Retrofit 2.6.0进行web服务调用。我得到了正确的API响应与所有响应代码(成功和错误案例)。我的问题是,当我在API调用之间断开互联网(Wifi/移动数据)时,从我编写的代码中,错误没有被正确捕获。大多数时候的错误是ConnectException和SocketException。

我已经尝试使用拦截器捕获错误,也尝试从发起调用的ViewModel中捕获错误。但在这里,异常也不会被捕获和处理。

代码语言:javascript
运行
复制
//ApiService
@GET(ApiUrl.API_DASHBOARD)
    suspend fun getHomeUiDetails(@Header("Authorization") authHeader: String): Response<HomeDetailsResponse>

//ConnectionBridge
suspend fun getHomeUiDetails(authToken: String): Response<HomeDetailsResponse> {
        return ApiServiceGenerator.BASIC_CLIENT_CONTRACT.getHomeUiDetails(authToken)
    }

// ViewModel
viewModelScope.launch(Dispatchers.IO) {
   val apiResponse = ApiConnectionBridge.getHomeUiDetails(SharedPrefUtils.getAuthToken(context))
        if (apiResponse.isSuccessful) {
           // success case
        } else {
            // error case
        }
}

object ApiServiceGenerator {

    val BASIC_CLIENT_CONTRACT: ApiService = ApiClient
        .getContract(
            ApiService::class.java,
            true,
            BuildConfig.BASE_URL
        )
}

object ApiClient {

    fun <T> getContract(clazz: Class<T>, isAuth: Boolean, baseUrl: String): T {
        return getRetrofitBuilder(baseUrl, getContractBuilder(isAuth)).create(clazz)
    }

    private fun getRetrofitBuilder(baseUrl: String, builder: OkHttpClient.Builder): Retrofit {
        val gson = GsonBuilder().serializeNulls().create()

        val loggingInterceptor = HttpLoggingInterceptor()
        loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY

        val okHttpClient = OkHttpClient.Builder()
            .addInterceptor { chain ->
                val original = chain.request()

                // Customize the request
                val request = original.newBuilder()
                request.header("Content-Type", "application/x-www-form-urlencoded")

                var response: Response? = null
                try {
                    response = chain.proceed(request.build())
                    response.cacheResponse()

                    // Customize or return the response

                    response!!
                } catch (e: ConnectException) {
                            Log.e("RETROFIT", "ERROR : " + e.localizedMessage)
                    chain.proceed(original)
                } catch (e: SocketException) {
                    Log.e("RETROFIT", "ERROR : " + e.localizedMessage)
                    chain.proceed(original)
                } catch (e: IOException) {
                    Log.e("RETROFIT", "ERROR : " + e.localizedMessage)
                    chain.proceed(original)
                } catch (e: Exception) {
                    Log.e("RETROFIT", "ERROR : " + e.localizedMessage)
                    chain.proceed(original)
                }
            }
            //            .cache(cache)
            .eventListener( object : EventListener() {
                override fun callFailed(call: Call, ioe: IOException) {
                    super.callFailed(call, ioe)
                }

            })
            .addInterceptor(loggingInterceptor)
            .readTimeout(60, TimeUnit.SECONDS)
            .connectTimeout(60, TimeUnit.SECONDS)
            .build()

        return Retrofit.Builder()
            .baseUrl(baseUrl)
            .client(okHttpClient)//getUnsafeOkHttpClient()
            .addConverterFactory(GsonConverterFactory.create(gson))
            .build()
    }
}

堆栈跟踪:

代码语言:javascript
运行
复制
2019-08-02 14:15:12.819 4157-4288/com.my.app E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-3
Process: com.my.app, PID: 4157
java.net.ConnectException: Failed to connect to my_url
    at okhttp3.internal.connection.RealConnection.connectSocket(RealConnection.java:248)
    at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:166)
    at okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:257)
    at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:135)
    at okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:114)
    at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
    at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
    at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:126)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
    at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:213)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
    at com.my.app.network.ApiClient$getRetrofitBuilder$okHttpClient$1.intercept(ApiClient.kt:50)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
    at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:254)
    at okhttp3.RealCall$AsyncCall.execute(RealCall.java:200)
    at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
    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)
 Caused by: java.net.ConnectException: failed to connect to my_url (port 80) from /:: (port 0) after 60000ms: connect failed: ENETUNREACH (Network is unreachable)
    at libcore.io.IoBridge.connect(IoBridge.java:137)
    at java.net.PlainSocketImpl.socketConnect(PlainSocketImpl.java:137)
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:390)
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:230)
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:212)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:436)
    at java.net.Socket.connect(Socket.java:621)
    at okhttp3.internal.platform.AndroidPlatform.connectSocket(AndroidPlatform.java:73)
    at okhttp3.internal.connection.RealConnection.connectSocket(RealConnection.java:246)
    at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:166) 
    at okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:257) 
    at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:135) 
    at okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:114) 
    at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42) 
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147) 
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121) 
    at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93) 
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147) 
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121) 
    at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93) 
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147) 
    at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:126) 
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147) 
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121) 
    at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:213) 
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147) 
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121) 
    at com.my.app.network.ApiClient$getRetrofitBuilder$okHttpClient$1.intercept(ApiClient.kt:50) 
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147) 
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121) 
    at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:254) 
    at okhttp3.RealCall$AsyncCall.execute(RealCall.java:200) 
    at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32) 
    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) 
 Caused by: android.system.ErrnoException: connect failed: ENETUNREACH (Network is unreachable)
    at libcore.io.Linux.connect(Native Method)
    at libcore.io.BlockGuardOs.connect(BlockGuardOs.java:118)
    at libcore.io.IoBridge.connectErrno(IoBridge.java:168)
    at libcore.io.IoBridge.connect(IoBridge.java:129)
    at java.net.PlainSocketImpl.socketConnect(PlainSocketImpl.java:137) 
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:390) 
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:230) 
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:212) 
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:436) 
    at java.net.Socket.connect(Socket.java:621) 
    at okhttp3.internal.platform.AndroidPlatform.connectSocket(AndroidPlatform.java:73) 
    at okhttp3.internal.connection.RealConnection.connectSocket(RealConnection.java:246) 
    at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:166) 
    at okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:257) 
    at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:135) 
    at okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:114) 
    at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42) 
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147) 
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121) 
    at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93) 
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147) 
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121) 
    at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93) 
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147) 
    at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:126) 
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147) 
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121) 
    at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:213) 
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147) 
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121) 
    at com.my.app.network.ApiClient$getRetrofitBuilder$okHttpClient$1.intercept(ApiClient.kt:50) 
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147) 
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121) 
    at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:254) 
    at okhttp3.RealCall$AsyncCall.execute(RealCall.java:200) 
    at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32) 
    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)
EN

回答 7

Stack Overflow用户

发布于 2019-09-13 15:07:48

好吧,这就是我所做的,只是为了减少try-catch垃圾复制

像这样声明我们的API调用方法

代码语言:javascript
运行
复制
@GET("do/smth")
suspend fun doSomething(): SomeCustomResponse

在单独的文件中

代码语言:javascript
运行
复制
suspend fun <T: Any> handleRequest(requestFunc: suspend () -> T): kotlin.Result<T> {
    return try {
        Result.success(requestFunc.invoke())
    } catch (he: HttpException) {
        Result.failure(he)
    }
}

用法:

代码语言:javascript
运行
复制
suspend fun doSmth(): kotlin.Result<SomeCustomResponse> {
    return handleRequest { myApi.doSomething() }
}

HTTP代码是由Retrofit处理的-如果responseCode不是2xx,它只抛出一个HttpException。所以我们应该做的就是捕捉这个异常。

我知道,这不是一个完美的解决方案,但让我们让杰克发明一些更好的东西。)

票数 17
EN

Stack Overflow用户

发布于 2019-11-15 16:33:10

您可以只添加一个CoroutineExceptionHandler来为您处理错误:

在您的ViewModel中:

代码语言:javascript
运行
复制
val coroutineExceptionHandler = CoroutineExceptionHandler{_, t -> {
  t.printStackTrace()
  showErrorOrSomething()
}}

viewModelScope.launch(Dispatchers.IO + coroutineExceptionHandler) {
   val apiResponse = ApiConnectionBridge.getHomeUiDetails(SharedPrefUtils.getAuthToken(context))
        if (apiResponse.isSuccessful) {
           // success case
        } else {
            // error case
        }
}
票数 4
EN

Stack Overflow用户

发布于 2019-11-18 18:23:39

除了亚瑟·马特塞戈尔的回答:

在我的例子中,对于错误的请求,API返回给我一条错误消息。对于这个场景,我需要在catch函数上捕获错误消息。我知道,编写try/catch来捕获函数看起来很丑陋,但它是有效的。

代码语言:javascript
运行
复制
private suspend fun <T : Any> handleRequest(requestFunc: suspend () -> T): Result<T> {
    return try {
        Result.success(requestFunc.invoke())
    } catch (httpException: HttpException) {
        val errorMessage = getErrorMessageFromGenericResponse(httpException)
        if (errorMessage.isNullOrBlank()) {
            Result.failure(httpException)
        } else {
            Result.failure(Throwable(errorMessage))
        }
    }
}

private fun getErrorMessageFromGenericResponse(httpException: HttpException): String? {
    var errorMessage: String? = null
    try {
        val body = httpException.response()?.errorBody()
        val adapter = Gson().getAdapter(GenericResponse::class.java)
        val errorParser = adapter.fromJson(body?.string())
        errorMessage = errorParser.errorMessage?.get(0)
    } catch (e: IOException) {
        e.printStackTrace()
    } finally {
        return errorMessage
    }
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57323111

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档