安卓开发封装处理Retrofit协程请求中的异常

上篇文章讲解了怎么使用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 ->

})

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

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是自定义的异常处理


使用Demo:
上篇文章:使用Kotlin协程配合Retrofit发送请求

热门相关:老子是癞蛤蟆