You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android开发中用Kotlin、Retrofit及协程处理结构不同的成功与错误响应

Kotlin+Retrofit+协程处理异构响应方案

1. 定义数据结构

首先定义和响应结构匹配的实体类,同时用密封类统一封装两种响应结果,方便后续类型判断:

// 成功响应实体
data class LoginSuccessResp(
    val access_token: String,
    val token_type: String
)

// 错误响应实体
data class ErrorResp(
    val detail: List<DetailItem>
) {
    data class DetailItem(
        val loc: List<String>,
        val msg: String,
        val type: String
    )
}

// 密封类封装所有请求结果类型
sealed class ApiResult<out T> {
    // 成功结果,携带成功响应数据
    data class Success<out T>(val data: T) : ApiResult<T>()
    // 业务错误结果,携带错误响应数据
    data class BizError(val error: ErrorResp) : ApiResult<Nothing>()
    // 可选:额外封装网络异常、解析异常等非业务错误
    data class OtherException(val throwable: Throwable) : ApiResult<Nothing>()
}

2. 定义Retrofit API接口方法

用Retrofit自带的Response泛型包装成功响应类型,方法定义为挂起函数适配协程调用:

interface AuthApi {
    @POST("auth/login")
    suspend fun login(@Body request: LoginRequest): Response<LoginSuccessResp>
}

// LoginRequest为登录请求参数实体,按需自行定义即可

3. 封装统一响应解析逻辑

写扩展函数统一处理两种响应的解析逻辑,避免重复代码:

import com.google.gson.Gson
import retrofit2.Response

fun <T> Response<T>.parseResponse(): ApiResult<T> {
    return try {
        if (isSuccessful) {
            // 2xx状态码走成功解析逻辑
            val body = body()
            if (body != null) {
                ApiResult.Success(body)
            } else {
                ApiResult.OtherException(RuntimeException("响应体为空"))
            }
        } else {
            // 非2xx状态码走错误解析逻辑
            val errorStr = errorBody()?.string() ?: return ApiResult.OtherException(RuntimeException("错误响应为空"))
            val errorResp = Gson().fromJson(errorStr, ErrorResp::class.java)
            ApiResult.BizError(errorResp)
        }
    } catch (e: Exception) {
        // 捕获网络异常、解析异常等问题
        ApiResult.OtherException(e)
    }
}

4. ViewModel层处理请求

在ViewModel中通过viewModelScope发起协程调用,处理后向View层分发结果:

class LoginViewModel(private val authApi: AuthApi) : ViewModel() {
    val loginResult = MutableLiveData<ApiResult<LoginSuccessResp>>()

    fun login(username: String, password: String) {
        viewModelScope.launch {
            val result = authApi.login(LoginRequest(username, password)).parseResponse()
            loginResult.postValue(result)
        }
    }
}

5. View层观察响应

在Activity/Fragment中观察LiveData,分场景处理不同结果:

class LoginActivity : AppCompatActivity() {
    private lateinit var viewModel: LoginViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 初始化ViewModel、布局等逻辑省略

        viewModel.loginResult.observe(this) { result ->
            when (result) {
                is ApiResult.Success -> {
                    // 处理成功逻辑:保存token、跳转页面等
                    val token = result.data.access_token
                    Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show()
                }
                is ApiResult.BizError -> {
                    // 处理业务错误逻辑:提示用户错误信息
                    val errorMsg = result.error.detail.firstOrNull()?.msg ?: "未知错误"
                    Toast.makeText(this, errorMsg, Toast.LENGTH_SHORT).show()
                }
                is ApiResult.OtherException -> {
                    // 处理异常逻辑:网络错误、解析错误等
                    Toast.makeText(this, "网络异常,请稍后重试", Toast.LENGTH_SHORT).show()
                }
            }
        }

        // 登录按钮点击调用viewModel.login()逻辑省略
    }
}

内容的提问来源于stack exchange,提问作者FrensisXh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 12:15:04