改造网络错误的异常处理

时间:2019-10-23 12:42:21

标签: android kotlin kotlin-coroutines

我想知道使用协程时在改造请求中处理网络错误的最佳方法是什么。

经典方法是在发出请求时在最高级别处理异常:

try {
    // retrofit request
} catch(e: NetworkException) {
    // show some error message
}

我发现此解决方案是错误的,它添加了许多样板代码,而是创建了一个返回错误响应的拦截器:

class ErrorResponse : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        return try {
            chain.proceed(request)
        } catch (e: Exception) {
            Snackbar.make(
                view,
                context.resources.getText(R.string.network_error),
                Snackbar.LENGTH_LONG
            ).show()
            Response.Builder()
                .request(request)
                .protocol(Protocol.HTTP_1_1)
                .code(599)
                .message(e.message!!)
                .body(ResponseBody.create(null, e.message!!))
                .build()
        }
    }
}

此解决方案要好一些,但是我认为它可以改进。

所以我的问题是:在没有大量样板代码的情况下(如果发生连接错误,最好使用全局处理程序)处理用户没有互联网连接的情况的正确方法是什么?

2 个答案:

答案 0 :(得分:2)

使用“结果”包装我的回复

sealed class Result<out T : Any> {
data class Success<out T : Any>(val value: T) : Result<T>()
data class Failure(val errorHolder:ErrorHolder) : Result<Nothing>()}

ErrorHolder:

sealed class ErrorHolder(override val message):Throwable(message){
 data class NetworkConnection(override val message: String, val throwable: Throwable) : ErrorHolder(message)
 data class BadRequest(override val message: String, val throwable: Throwable) : ErrorHolder(message)
}

用于处理例外的扩展

suspend fun <T, R> Call<T>.awaitResult(map: (T) -> R): Result<R> = suspendCancellableCoroutine { continuation ->
try {
    enqueue(object : Callback<T> {
        override fun onFailure(call: Call<T>, throwable: Throwable) {
            errorHappened(throwable)
        }

        override fun onResponse(call: Call<T>, response: Response<T>) {
            if (response.isSuccessful) {
                try {
                    continuation.resume(Result.Success(map(response.body()!!)))
                } catch (throwable: Throwable) {
                    errorHappened(throwable)
                }
            } else {
                errorHappened(HttpException(response))
            }
        }

        private fun errorHappened(throwable: Throwable) {
            continuation.resume(Result.Failure(asNetworkException(throwable)))
        }
    })
} catch (throwable: Throwable) {
    continuation.resume(Result.Failure(asNetworkException(throwable)))
}

continuation.invokeOnCancellation {
    cancel()
}}

这就是我进行api调用的方式:

suspend fun fetchUsers(): Result<List<User>> {
    return service.getUsers().awaitResult { usersResponseDto ->
        usersResponseDto.toListOfUsers()
    }
}

答案 1 :(得分:1)

通过实施Interceptor,您的做法正确。但是只要稍作更改,您就可以使用以下示例类:

class NetworkConnectionInterceptor(val context: Context) : Interceptor {

    @Suppress("DEPRECATION")
    private val isConnected: Boolean
        get() {
            var result = false
            val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                cm?.run {
                    cm.getNetworkCapabilities(cm.activeNetwork)?.run {
                        result = when {
                            hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
                            hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
                            hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
                            else -> false
                        }
                    }
                }
            } else {
                cm?.run {
                    cm.activeNetworkInfo?.run {
                        if (type == ConnectivityManager.TYPE_WIFI) {
                            result = true
                        } else if (type == ConnectivityManager.TYPE_MOBILE) {
                            result = true
                        }
                    }
                }
            }
            return result
        }

    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        if (!isConnected) {
            // Throwing your custom exception
            // And handle it on onFailure
        }

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

然后将其添加到您的OkHttpClient.Builder()

.addInterceptor(NetworkConnectionInterceptor(context));

如果失败,您可以使用onFailure这样的方法来处理它:

override fun onFailure(call: Call<BaseModel>, t: Throwable) {
    if (t is NoConnectivityException) {
        // Handle it here :)
    }
}