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

Retrofit2返回结果无法创建ApiResponse.Success实例问题

问题解决:Retrofit解析密封类ApiResponse时Success状态抛出异常

问题场景

采用MVVM+Repository模式开发,使用泛型密封类ApiResponse作为客户端统一响应封装。调用Retrofit2登录接口后,创建ApiResponse.Success实例时持续抛出异常,其余状态(Loading/Error/ExceptionError)均正常工作。

异常信息:

无法创建com.example.ApiResponse类的实例。注册InstanceCreator或TypeAdapter,或者添加无参构造函数可能解决此问题。

错误原因

  1. 密封类与Gson解析冲突:ApiResponse是密封类,Gson默认无法识别并正确实例化其子类。Retrofit通过Gson解析响应时,会尝试直接实例化ApiResponse父类,但父类是密封类且无无参构造函数,导致实例化失败。
  2. 职责混淆:将客户端状态封装类ApiResponse直接作为Retrofit的响应类型,而ApiResponse包含客户端特有的状态(如Loading、ExceptionError),这些并非服务器返回的结构,Gson无法解析非服务器定义的字段。

解决方案

最清晰的解决方式是将服务器响应实体与客户端状态封装类分离:

  • 定义专门的服务器响应实体类,对应服务器返回的字段结构;
  • Retrofit返回该实体类的响应,在Repository层手动转换为ApiResponse的子类实例。

代码修改示例

1. 创建服务器响应实体类

// 对应服务器返回的原始响应结构
data class ServerResponse<T>(
    val code: Int? = null,
    val status: String? = null,
    val message: String? = null,
    val data: T? = null,
    val errors: Map<String, ArrayList<String>>? = null
)

2. 修改AuthService接口

将Retrofit的返回类型改为服务器响应实体:

interface AuthService {
    @POST("accounts/login")
    suspend fun getTokenPair(@Body userLoginCred: UserLoginCred): Response<ServerResponse<UserTokenPair>>
}

3. 调整AuthRepository逻辑

在Repository层解析服务器响应后,手动创建ApiResponse子类实例:

if (NetworkUtil.isInternetAvailable(context)!!) {
    try {
        val response = authService.getTokenPair(userLoginCred)
        if (response.isSuccessful && response.body() != null) {
            val serverResponse = response.body()!!
            _userTokenPairResponse.postValue(ApiResponse.Success(serverResponse.data))
        } else if (response.errorBody() != null) {
            // 使用TypeToken避免类型擦除问题
            val errorMap = Gson().fromJson(
                response.errorBody()!!.string(),
                object : TypeToken<Map<String, ArrayList<String>>>() {}.type
            )
            _userTokenPairResponse.postValue(ApiResponse.Error(errorMap))
        } else {
            _userTokenPairResponse.postValue(ApiResponse.Loading())
        }
    } catch (e: Exception) {
        _userTokenPairResponse.postValue(ApiResponse.ExceptionError(e.message))
    }
} else {
    _userTokenPairResponse.postValue(ApiResponse.ExceptionError("设备未连接网络。"))
}

替代方案(快速修复)

若不想分离实体类,可给ApiResponse的所有子类添加无参构造函数,并确保构造参数带默认值:

sealed class ApiResponse<T> (
    val code: Int? = null,
    val status: String? = null,
    val message: String? = null,
    val data: T? = null,
    val errors: Map<String, ArrayList<String>>? = null,
    val exceptionMessage: String? = null,
) {
    class Loading<T>: ApiResponse<T>()

    // 添加默认值,允许无参实例化
    class Success<T> (data: T? = null): ApiResponse<T> (data = data)

    class Error<T> (errors: Map<String, ArrayList<String>>? = null): ApiResponse<T> (errors = errors)

    class ExceptionError<T> (exceptionMessage: String? = null): ApiResponse<T> (exceptionMessage = exceptionMessage)
}

但此方法仅解决实例化问题,仍存在客户端状态与服务器响应职责混淆的问题,不推荐长期使用。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 04:55:14