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
相关产品推荐
相关产品推荐

