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

Android中使用Retrofit处理多类型响应的问题排查

解决Retrofit处理两种不同响应结构的问题

问题根源

你当前方案的核心问题在于自定义的BaseLevelsEntity不符合服务器实际返回的响应结构:

  • 服务器成功时直接返回LevelsEntity对应的JSON数组,错误时直接返回ResponseError对应的JSON对象;
  • 而你定义的BaseLevelsEntity是一个包含levelsEntity和responseError两个字段的对象,服务器根本不会返回这种格式,导致Retrofit无法完成解析,最终response.body()始终为null。
  • 另外,suspend函数返回Call是错误用法,Retrofit的suspend函数不需要用Call包装,直接返回响应类型即可。

修复方案

1. 移除无效的BaseLevelsEntity

直接删除这个封装类,它和服务器响应结构不匹配,无法被Retrofit解析。

2. 修改Retrofit接口定义

将接口返回类型改为ResponseBody,获取原始响应内容后手动判断解析类型:

@POST("/webservice/rest/server.php")
suspend fun getPopularLevelsInLessonsF(
    @Query("mdwsrestformat") mdwsrestformat: String?,
    @Field("wsfunction") wsfunction: String?,
    @Field("wstoken") wstoken: String?,
    @Field("userid") userid: Int?
): ResponseBody

3. 重构Repository解析逻辑

在Repository中手动解析响应,区分成功数组和错误对象:

class LessonsRepositoryImpl(
    private val lessonsRemoteDatasource: LessonsRemoteDatasource
) : LessonsRepository {
    private val gson = Gson()

    override suspend fun getLevelsInLessonsF(
        wstoken: String,
        userid: Int
    ): Resource<Any> {
        return try {
            // 调用接口获取原始响应
            val responseBody = lessonsRemoteDatasource.getPopularLevelsInLessonsF(
                "json", // 替换为实际的mdwsrestformat参数值
                "your_wsfunction", // 替换为实际的wsfunction参数值
                wstoken,
                userid
            )
            val responseContent = responseBody.string()

            // 判断响应类型:数组为成功,对象为错误
            val jsonElement = gson.fromJson(responseContent, JsonElement::class.java)
            when {
                jsonElement.isJsonArray -> {
                    // 解析为成功实体
                    val levelsEntity = gson.fromJson(responseContent, LevelsEntity::class.java)
                    Resource.Success(levelsEntity)
                }
                jsonElement.isJsonObject -> {
                    // 解析为错误实体
                    val error = gson.fromJson(responseContent, ResponseError::class.java)
                    Resource.Error(error.errorcode ?: "unknown", error.message)
                }
                else -> Resource.Error("unknown_response")
            }
        } catch (e: IOException) {
            // 处理网络异常
            Resource.Error("network_error", e.message)
        } catch (e: JsonSyntaxException) {
            // 处理解析异常
            Resource.Error("parse_error", e.message)
        }
    }
}

// 优化Resource密封类,更清晰区分状态
sealed class Resource<out T> {
    data class Success<out T>(val data: T) : Resource<T>()
    data class Error(val errorCode: String, val message: String? = null) : Resource<Nothing>()
}

4. 处理HTTP错误码的情况

如果服务器在错误时返回非2xx状态码,需额外处理errorBody的解析:

// 替换Repository中的try-catch块内容为:
val response = lessonsRemoteDatasource.getPopularLevelsInLessonsF(/* 参数 */)
if (response.isSuccessful) {
    // 解析成功响应体,逻辑同上
    val responseContent = response.string()
    // ... 解析逻辑
} else {
    // 处理非2xx响应的错误体
    val errorContent = response.errorBody()?.string()
    errorContent?.let {
        val error = gson.fromJson(it, ResponseError::class.java)
        Resource.Error(error.errorcode ?: "unknown", error.message)
    } ?: Resource.Error(response.code().toString(), response.message())
}

关键注意事项

  • 不要用自定义封装类强制匹配服务器不返回的结构,Retrofit解析严格遵循JSON结构与实体类的映射;
  • Retrofit的suspend函数直接返回响应类型即可,无需用Call包装,也不需要调用execute();
  • 手动解析时要处理网络异常、JSON格式异常等情况,避免崩溃。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 10:05:32