上篇文章讲解了怎么使用Kotlin的协程配合Retrofit发起网络请求,使用也是非常方便,但是在处理请求异常还不是很人性化。这篇文章,我们将处理异常的代码进行封装,以便对异常情况返回给页面,提供更加友好的提示。

编写拓展方法

我们写一个扩展(全局)方法,就叫ViewModelExt.kt,在下面创建方法。

/**
* ViewModel扩展方法:启动协程
* @param block 协程逻辑
* @param onError 错误回调方法
* @param onComplete 完成回调方法
*/
fun ViewModel.launch(
block: suspend CoroutineScope.() -> Unit,
onError: (e: Throwable) -> Unit = { _: Throwable -> },
onComplete: () -> Unit = {}
) {
viewModelScope.launch(
CoroutineExceptionHandler { _, throwable ->
run {
// 这里统一处理错误
ExceptionUtil.catchException(throwable)
onError(throwable)
}
}
) {
try {
block.invoke(this)
} finally {
onComplete()
}
}
}

统一异常处理

拓展方法里面对写成过程做了统一拦截,在onComplete方法可以做统一的异常处理

/**
* 异常工具类
* @author ssq
*/
object ExceptionUtil {

/**
* 处理异常,toast提示错误信息
*/
fun catchException(e: Throwable) {
e.printStackTrace()
when (e) {
is HttpException -> {
catchHttpException(e.code())
}
is SocketTimeoutException -> {
showToast(R.string.common_error_net_time_out)
}
is UnknownHostException, is NetworkErrorException -> {
showToast(R.string.common_error_net)
}
is MalformedJsonException, is JsonSyntaxException -> {
showToast(R.string.common_error_server_json)
}
is InterruptedIOException -> {
showToast("服务器连接失败,请稍后重试")
}
// 自定义接口异常
is ApiException -> {
showToast(e.message?:"", e.code)
}
is ConnectException -> {
showToast( "连接服务器失败" )
}
else -> {
showToast("${MyApplication.instance.getString(
R.string.common_error_do_something_fail
)}${e::class.java.name}")
}
}
}

/**
* 处理网络异常
*/
fun catchHttpException(errorCode: Int) {
if (errorCode in 200 until 300) return// 成功code则不处理
showToast(
catchHttpExceptionCode(
errorCode
), errorCode
)
}

/**
* toast提示
*/
private fun showToast(@StringRes errorMsg: Int, errorCode: Int = -1) {
showToast(
MyApplication.instance.getString(
errorMsg
), errorCode
)
}

/**
* toast提示
*/
private fun showToast(errorMsg: String, errorCode: Int = -1) {
if (errorCode == -1) {
ToastUtils.showShort(errorMsg)
} else {
ToastUtils.showShort("$errorCode$errorMsg")
}
}

/**
* 处理网络异常
*/
private fun catchHttpExceptionCode(errorCode: Int): Int = when (errorCode) {
in 500..600 -> R.string.common_error_server
in 400 until 500 -> R.string.common_error_request
else -> R.string.common_error_request
}
}

ApiException是自定义的异常类

如何使用

我们在一个ViewModel中可以这样使用

fun login(user: User) = launch({
val resultData = RetrofitClient.userService.login(user)
if (resultData.code == 20000) {
userInfo.value = RetrofitClient.userService.getUserInfo().data
}
},onError = { e: Throwable ->

})

注意:我们在ViewModel的拓展方法中已经进行了协程调用操作,所以这里直接调用拓展方法即可。

如果是返回的结果不对,可以这样做

fun login(user: User) = launch({
val resultData = RetrofitClient.userService.login(user)
if (resultData.code == 20000) {
userInfo.value = RetrofitClient.userService.getUserInfo().data
} else {
throw ApiException(-1, "返回结果出错")
}
},onError = { e: Throwable ->
// 在这里进行处理
})

ApiException是自定义的异常处理,该异常同样会被拦截,进入onError中,可以在这里进行处理。