Android 网络错误改造的异常处理
我想知道在使用协同路由时,处理改装请求中的网络错误的最佳方法是什么 经典方法是在发出请求时在最高级别处理异常:Android 网络错误改造的异常处理,android,kotlin,kotlin-coroutines,Android,Kotlin,Kotlin Coroutines,我想知道在使用协同路由时,处理改装请求中的网络错误的最佳方法是什么 经典方法是在发出请求时在最高级别处理异常: try { // retrofit request } catch(e: NetworkException) { // show some error message } 我发现这个解决方案是错误的,它添加了很多样板代码,相反,我创建了一个返回错误响应的拦截器: class ErrorResponse : Interceptor { override fun
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()
}
}
}
这个解决方案稍微好一点,但是我认为它可以改进
所以我的问题是:当用户没有互联网连接时,如果没有很多样板代码(最好是在连接错误的情况下使用全局处理程序),正确的处理方法是什么。但只要稍作更改,您就可以使用以下示例类:
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()
:
如果出现故障,您可以使用以下方法处理故障:
override fun onFailure(call: Call<BaseModel>, t: Throwable) {
if (t is NoConnectivityException) {
// Handle it here :)
}
}
override-fun-onFailure(call:call,t:Throwable){
if(t为无连接异常){
//在这里处理:)
}
}
使用结果包装我的响应
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>()}
处理例外的扩展
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()
}}
首先,我们应该创建一个数据类来模拟响应体
data class HttpErrorEntity(
@SerializedName("message") val errorMessage: String,
@SerializedName("status") val errorCode: Int
)
下面是asNetworkException
实现:
private fun asNetworkException(ex: Throwable): ErrorHolder {
return when (ex) {
is IOException -> {
ErrorHolder.NetworkConnection(
"No Internet Connection"
)
}
is HttpException -> extractHttpExceptions(ex)
else -> ErrorHolder.UnExpected("Something went wrong...")
}
}
private fun extractHttpExceptions(ex: HttpException): ErrorHolder {
val body = ex.response()?.errorBody()
val gson = GsonBuilder().create()
val responseBody= gson.fromJson(body.toString(), JsonObject::class.java)
val errorEntity = gson.fromJson(responseBody, HttpErrorEntity::class.java)
return when (errorEntity.errorCode) {
ErrorCodes.BAD_REQUEST.code ->
ErrorHolder.BadRequest(errorEntity.errorMessage)
ErrorCodes.INTERNAL_SERVER.code ->
ErrorHolder.InternalServerError(errorEntity.errorMessage)
ErrorCodes.UNAUTHORIZED.code ->
ErrorHolder.UnAuthorized(errorEntity.errorMessage)
ErrorCodes.NOT_FOUND.code ->
ErrorHolder.ResourceNotFound(errorEntity.errorMessage)
else ->
ErrorHolder.Unknown(errorEntity.errorMessage)
}
}
我希望避免手动检查网络连接,因为它需要单独的权限。这也不能解决您必须处理异常的问题。@Daniel如果不手动检查网络连接,您必须检查异常实例的改装调用
IOException
方法。虽然这包括另一个错误,但不仅仅是脱机错误。您可以添加asNetworkException()乐趣实现吗?请有兴趣了解如何处理错误案例感谢Sprivate fun asNetworkException(throwable:throwable):ErrorHolder{return ErrorHolder.NetworkConnection(throwable=throwable,message=throwable.message?:“Empty error message”)}@MohammadSianaki您缺少错误保持器上的意外值,ErrorCodes类是否这只是一堆静态字符串,如400 for error等?@MohammadSianaki我在结果上使用此字符串时也会出错。成功:在构造函数成功中键入为T绑定的参数(值:T)不满足:推断类型R不是的子类型Any@martinseal1987kotlin标准库有自己的结果类型,请确保您正在导入自己的类型,而不是从标准库导入的类型。
suspend fun fetchUsers(): Result<List<User>> {
return service.getUsers().awaitResult { usersResponseDto ->
usersResponseDto.toListOfUsers()
}
}
{
"error" : {
"status" : 502,
"message" : "Bad gateway."
}
}
data class HttpErrorEntity(
@SerializedName("message") val errorMessage: String,
@SerializedName("status") val errorCode: Int
)
private fun asNetworkException(ex: Throwable): ErrorHolder {
return when (ex) {
is IOException -> {
ErrorHolder.NetworkConnection(
"No Internet Connection"
)
}
is HttpException -> extractHttpExceptions(ex)
else -> ErrorHolder.UnExpected("Something went wrong...")
}
}
private fun extractHttpExceptions(ex: HttpException): ErrorHolder {
val body = ex.response()?.errorBody()
val gson = GsonBuilder().create()
val responseBody= gson.fromJson(body.toString(), JsonObject::class.java)
val errorEntity = gson.fromJson(responseBody, HttpErrorEntity::class.java)
return when (errorEntity.errorCode) {
ErrorCodes.BAD_REQUEST.code ->
ErrorHolder.BadRequest(errorEntity.errorMessage)
ErrorCodes.INTERNAL_SERVER.code ->
ErrorHolder.InternalServerError(errorEntity.errorMessage)
ErrorCodes.UNAUTHORIZED.code ->
ErrorHolder.UnAuthorized(errorEntity.errorMessage)
ErrorCodes.NOT_FOUND.code ->
ErrorHolder.ResourceNotFound(errorEntity.errorMessage)
else ->
ErrorHolder.Unknown(errorEntity.errorMessage)
}
}