如何使用GsonConverter全局处理Retrofit响应解析错误
解决Retrofit自定义Gson解析器无法获取响应URL的问题
你在使用RxJava、Retrofit和GsonConverterFactory全局处理响应解析错误时,自定义的GsonResponseBodyConverter无法获取请求URL,导致无法将错误关联到具体接口上报到Analytics服务。下面提供两种可行的实现方案:
方案一:通过Interceptor将URL存入响应头
实现步骤
- 自定义Interceptor,在响应返回时把请求URL写入响应头
- 在自定义Converter中从ResponseBody的头信息里读取URL
1. 编写URL拦截器
class UrlAddingInterceptor : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val request = chain.request() val response = chain.proceed(request) // 自定义头字段存储请求URL,避免与现有头冲突 return response.newBuilder() .addHeader("X-Request-Url", request.url.toString()) .build() } }
2. 给Retrofit添加该拦截器
val retrofit = Retrofit.Builder() .baseUrl(BASE_URL) .addInterceptor(UrlAddingInterceptor()) // 注意顺序,放在其他可能修改响应头的拦截器之前 .addConverterFactory(GsonConverterFactory.create(gson)) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build()
3. 修改自定义Converter获取URL
class GsonResponseBodyConverter<T>( private val gson: Gson, private val adapter: TypeAdapter<T>, private val analytics: Analytics ) : Converter<ResponseBody, T> { private val errorResponseSubject = PublishSubject.create<Pair<Throwable, String>>() init { val errorResponseFlowable = errorResponseSubject.toFlowable(BackpressureStrategy.BUFFER) val disposable = Flowables.combineLatest( errorResponseFlowable, Mapper::exceptionToEntity ) .flatMap { analytics.saveError(it).andThen(Flowable.just(Unit)) } .subscribeOn(Schedulers.IO) .subscribe({ print(it) }, RxThrowable.printStackTrace(logger)) } @Throws(IOException::class) override fun convert(value: ResponseBody): T { val jsonReader = gson.newJsonReader(value.charStream()) // 从响应头中读取URL,默认值处理避免空指针 val requestUrl = value.headers().get("X-Request-Url") ?: "Unknown URL" return value.use { runCatching { val result = adapter.read(jsonReader) if (jsonReader.peek() != JsonToken.END_DOCUMENT) { throw JsonIOException("JSON document was not fully consumed.") } result }.getOrElse { error -> errorResponseSubject.onNext(Pair(error, requestUrl)) throw error } } } }
方案二:自定义CallAdapter统一处理错误(推荐)
通过自定义RxJava CallAdapter,在响应流阶段捕获错误,直接从Call或Response对象中获取URL,不需要修改Converter。
实现步骤
- 自定义CallAdapter包装类,处理错误并上报Analytics
- 编写CallAdapterFactory供Retrofit使用
1. 自定义CallAdapter包装类
class RxErrorHandlingCallAdapterWrapper<R>( private val delegate: CallAdapter<R, Flowable<R>>, private val analytics: Analytics ) : CallAdapter<R, Flowable<R>> { override fun responseType(): Type = delegate.responseType() override fun adapt(call: Call<R>): Flowable<R> { return delegate.adapt(call) .onErrorResumeNext { throwable -> // 统一处理所有流中的错误 val requestUrl = when (throwable) { // HTTP错误从Response中取URL is HttpException -> throwable.response()?.raw()?.request?.url.toString() ?: "Unknown URL" // 解析错误直接从Call的Request中取URL is JsonIOException, is JsonSyntaxException -> call.request().url.toString() ?: "Unknown URL" // 其他错误默认值 else -> "Unknown URL" } // 转换错误实体并上报 val errorEntity = Mapper.exceptionToEntity(Pair(throwable, requestUrl)) analytics.saveError(errorEntity) .andThen(Flowable.error(throwable)) // 上报后继续抛出错误,不影响业务逻辑 } } }
2. 编写CallAdapterFactory
class RxErrorHandlingCallAdapterFactory(private val analytics: Analytics) : CallAdapter.Factory() { override fun get( returnType: Type, annotations: Array<Annotation>, retrofit: Retrofit ): CallAdapter<*, *>? { // 获取Retrofit默认的RxJava CallAdapter val delegate = retrofit.nextCallAdapter(this, returnType, annotations) as? CallAdapter<*, Flowable<*>> return delegate?.let { RxErrorHandlingCallAdapterWrapper(it, analytics) } } }
3. 替换Retrofit的CallAdapterFactory
val retrofit = Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create(gson)) // 替换原有的RxJavaCallAdapterFactory .addCallAdapterFactory(RxErrorHandlingCallAdapterFactory(analytics)) .build()
方案对比
- 方案一:侵入性低,适合需要在Converter层处理解析逻辑的场景,但依赖响应头传递URL,需注意拦截器顺序。
- 方案二:更符合Retrofit的设计理念,统一处理所有RxJava流错误,无需修改Converter,推荐用于全局错误上报场景。
内容的提问来源于stack exchange,提问作者Zafar Abdulloev
相关产品推荐
相关产品推荐

