安卓開發封裝處理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發送請求


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM