Retrofit POST请求空响应体致Android App崩溃问题求助
相关代码
API代码
object RetrofitInstance { val nullOnEmptyConverterFactory = object : Converter.Factory() { fun converterFactory() = this override fun responseBodyConverter(type: Type, annotations: Array<out Annotation>, retrofit: Retrofit) = object : Converter<ResponseBody, Any?> { val nextResponseBodyConverter = retrofit.nextResponseBodyConverter<Any?> (converterFactory(), type, annotations) override fun convert(value: ResponseBody) = if (value.contentLength() != 0L) nextResponseBodyConverter.convert(value) else null } } val loggingInterceptor: HttpLoggingInterceptor = HttpLoggingInterceptor { message -> // Log the request URL println("$message") }.setLevel(Level.BODY) private val httpClient = OkHttpClient.Builder() .addInterceptor(loggingInterceptor) .build() private val retrofit by lazy { Retrofit.Builder() .baseUrl(BASE_URL) .client(httpClient) .addConverterFactory(nullOnEmptyConverterFactory) .addConverterFactory(GsonConverterFactory.create()) .build() } val api: API by lazy { retrofit.create(API::class.java) } }
MainViewModelFactory代码
class MainViewModelFactory(private val repository: Repository): ViewModelProvider.Factory { override fun <T : ViewModel?> create(modelClass: Class<T>): T { return MainViewModel(repository) as T } }
MainViewModel代码
fun pushClear(uniqueId: String, DELIVERED: Int = 0, delno: String) { viewModelScope.launch { val call = repository.pushClear(uniqueId, DELIVERED, delno) call.enqueue(object : Callback<Unit> { override fun onResponse(call: Call<Unit>, response: Response<Unit>){ } override fun onFailure(call: Call<Unit>, t: Throwable) { } }) // val response = repository.pushClear(uniqueId, DELIVERED, delno) // myPost6.value = response } }
Repository代码
suspend fun pushClear(uniqueId: String, DELIVERED: Int = 0, delno: String): Call<Unit> { return RetrofitInstance.api.pushClear(uniqueId, DELIVERED, delno) }
POST请求定义代码
@POST("/?q=8") suspend fun pushClear( @Query("ID") uniqueId: String, @Query("p1") DELIVERED: Int = 0, @Query("p2") delno: String ): Call<Unit>
POST请求调用代码
val repository = Repository() val viewModelFactory = MainViewModelFactory(repository) viewModel = ViewModelProvider(this, viewModelFactory).get(MainViewModel::class.java) viewModel.pushClear(uniqueId,0, DELNO)
解决方法
问题根源是自定义空响应转换器实现错误,以及suspend函数与Call返回值混用导致Retrofit解析空响应时抛出EOFException。按以下步骤修改:
1. 修复自定义空响应转换器
修正nullOnEmptyConverterFactory的实现,避免循环引用,正确处理空响应:
val nullOnEmptyConverterFactory = object : Converter.Factory() { override fun responseBodyConverter(type: Type, annotations: Array<out Annotation>, retrofit: Retrofit): Converter<ResponseBody, *>? { // 获取下一个转换器(即GsonConverter) val delegate = retrofit.nextResponseBodyConverter<Any>(this, type, annotations) return Converter { body -> if (body.contentLength() == 0L) { // 空响应返回null,跳过Gson解析 null } else { // 非空响应交给Gson处理 delegate.convert(body) } } } }
2. 调整ConverterFactory的添加顺序
在Retrofit构建时,必须将自定义转换器放在Gson转换器之前,确保空响应先被拦截:
private val retrofit by lazy { Retrofit.Builder() .baseUrl(BASE_URL) .client(httpClient) .addConverterFactory(nullOnEmptyConverterFactory) // 自定义转换器在前 .addConverterFactory(GsonConverterFactory.create()) // Gson转换器在后 .build() }
3. 修正API接口的suspend函数返回值
Retrofit的suspend函数不需要返回Call<Unit>,直接返回Unit即可,异常通过抛出方式处理:
@POST("/?q=8") suspend fun pushClear( @Query("ID") uniqueId: String, @Query("p1") DELIVERED: Int = 0, @Query("p2") delno: String ): Unit
4. 更新Repository代码
去掉返回值Call<Unit>,直接调用接口方法:
suspend fun pushClear(uniqueId: String, DELIVERED: Int = 0, delno: String) { RetrofitInstance.api.pushClear(uniqueId, DELIVERED, delno) }
5. 修改ViewModel中的调用逻辑
用try-catch捕获异常,替代enqueue回调(suspend函数无需手动处理异步):
fun pushClear(uniqueId: String, DELIVERED: Int = 0, delno: String) { viewModelScope.launch { try { repository.pushClear(uniqueId, DELIVERED, delno) // 这里添加请求成功后的逻辑,比如更新UI、提示用户等 } catch (e: Exception) { // 处理异常,比如网络错误、解析错误等 e.printStackTrace() } } }
内容的提问来源于stack exchange,提问作者helpVulcan
相关产品推荐
相关产品推荐

