Retrofit|Retrofit 2.0 Gson解析数据的特殊处理
Retrofit 2.0的使用及封装见 Retrofit 2.0的封装与异常处理
开发过程中有时会遇到这种需求,即后台返回的数据,我们需要做进一步处理,比如字段的替换、筛选或者排序,结合上一篇Retrofit 2.0的使用,通常情况下我们在onResponse
回调后获得后台返回的model,然后再执行一些特殊处理,如果是耗时操作,我们还需要开个线程...这种方式既不灵活也不优雅。
我们能不能在http获取数据后,onResponse
回调之前,即Retrofit2.0 Gson解析时“顺便”把数据处理了?这样既保证了在子线程又能保持onResponse
回调中代码的清爽。
下面看我的解决方案:
interface ResponseConverter {
fun onGsonResponseBody(value: T): T
}
- 先定义一个接口,如过某个请求需要特殊处理,实现该接口并在
onGsonResponseBody
回调中进行处理,这里不需要考虑线程,因为该回调本身就是在子线程中执行的,这个后面会讲,示例代码如下:
class MarketResponseConverter : ResponseConverter> {
override fun onGsonResponseBody(httpResponse: HttpResponse): HttpResponse{
httpResponse.msg = "this is test message"
return httpResponse
}
}
- 再定义一个注解,该注解是加到Retrofit2.0定义的请求上,好让
GsonResponseBodyConverter
知道我这个请求需要单独做处理,注解的属性即上面我们定义的ResponseConverter
的Class
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class ResponseProcess(val clazz: KClass>)
interface ApiService {
@POST("market/getMarket")
@ResponseProcess(MarketResponseConverter::class)
fun getMarket(): Call>
- 重点,自定义
GsonConverterFactory
与GsonResponseBodyConverter
, Gson解析完后不直接返回数据,而是判断请求是否有加ResponseProcess
注解,如果有ResponseProcess
注解则通过反射直接获取该注解属性的ResponseConverter
数据处理类,执行onGsonResponseBody
后再返回数据。
class CustomGsonConverterFactory(val gson: Gson) : Converter.Factory() {
companion object {
fun create(): CustomGsonConverterFactory {
return create(Gson())
}private fun create(gson: Gson?): CustomGsonConverterFactory {
if (gson == null) throw NullPointerException("gson == null")
return CustomGsonConverterFactory(gson)
}
}override fun responseBodyConverter(type: Type?, annotations: Array?, retrofit: Retrofit?): Converter? {
/** 直接将annotations作为参数传递给CustomGsonResponseBodyConverter**/
return CustomGsonResponseBodyConverter(gson, annotations, gson.getAdapter(TypeToken.get(type)))
}override fun requestBodyConverter(type: Type?, parameterAnnotations: Array?,
methodAnnotations: Array?, retrofit: Retrofit?): Converter<*, RequestBody>? {
return CustomGsonRequestBodyConverter(gson, gson.getAdapter(TypeToken.get(type)))
}
}
class CustomGsonResponseBodyConverter(private val gson: Gson, private val annotations: Array?, private val adapter: TypeAdapter) : Converter {override fun convert(value: ResponseBody): T {
val response = value.string()
val httpStatus = gson.fromJson(response, HttpStatus::class.java)
/** 本节无关,异常统一处理,见上篇《Retrofit 2.0的封装与异常处理》**/
if (!httpStatus.isSuccess()) {
value.close()
throw ApiException(httpStatus.code, httpStatus.msg)
}
val contentType = value.contentType()
val charset = contentType?.charset(UTF_8) ?: UTF_8
val inputStream = ByteArrayInputStream(response.toByteArray())
val reader = InputStreamReader(inputStream, charset!!)
val jsonReader = gson.newJsonReader(reader)value.use { _ ->
val result = adapter.read(jsonReader)
if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
throw JsonIOException("JSON document was not fully consumed.")
}/** 遍历annotations判断该请求是否存在ResponseProcess注释 **/
if (annotations != null) {
for (annotation: Annotation in annotations) {
if (annotation is ResponseProcess) {
@Suppress("UNCHECKED_CAST")
/** 反射获取ResponseConverter的实例 **/
val instance = annotation.clazz.createInstance() as ResponseConverter
/** 数据经过onGsonResponseBody处理后再返回 **/
return instance.onGsonResponseBody(result)
}
}
}
return result
}
}
}
【Retrofit|Retrofit 2.0 Gson解析数据的特殊处理】测试后你会发现该请求的HttpResponse的msg已经被我们替换成"this is test message"。
推荐阅读
- EffectiveObjective-C2.0|EffectiveObjective-C2.0 笔记 - 第二部分
- 感恩日记第111篇2020.02.06
- 幸福2.0六组90天践行总纲指导方针
- BNC公链|BNC公链 | Eth2.0测试网Topaz已质押超100万枚ETH
- retrofit2-kotlin-coroutines-adapter|retrofit2-kotlin-coroutines-adapter 超时引起的崩溃
- 千人共读《改变的力量》|千人共读《改变的力量》 打卡第一天2020.02.02
- 我们需要正能量
- 2017.12.06日记
- 四步打造独角兽V2.0(四)---避开产品荒漠
- G225期|G225期 2.0践行 20190512日 周检视