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

