4. 自定义 Converter 工厂:Gson / Moshi / Jackson 与自定义解析器
Retrofit 通过 Converter.Factory 将 HTTP 响应体(ResponseBody)反序列化为 Java 对象,并将请求体(RequestBody)序列化为网络字节流。这一节深入剖析三种主流 JSON 转换器工厂的原理、配置、性能差异,并亲手实现一个自定义解析器以支持非常规数据格式(如 XML、Protobuf 或定制加密流)。
4.1 转换器工厂的工作原理
Retrofit 在构建时通过 .addConverterFactory() 注册多个工厂,运行时按注册顺序依次调用每个工厂的 requestBodyConverter() 和 responseBodyConverter() 方法,直到某个工厂返回非空转换器为止。其核心接口如下:
关键接口:
Converter.Factory 中的两个抽象方法需要子类实现:
public @Nullable Converter<ResponseBody, ?> responseBodyConverter(
Type type, Annotation[] annotations, Retrofit retrofit);
public @Nullable Converter<?, RequestBody> requestBodyConverter(
Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit);
一旦找到匹配的转换器,后续的所有请求/响应都会使用该转换器;如果所有工厂都无法处理,则抛出 IllegalArgumentException。
4.2 三大主流 JSON 工厂对比
| 特性 | GsonConverterFactory | MoshiConverterFactory | JacksonConverterFactory |
|---|---|---|---|
| 底层库 | Gson (Google) | Moshi (Square) | Jackson (FasterXML) |
| 反射策略 | 反射 + 内置适配器 | 反射 + 代码生成 (Kotlin) | 反射 + 注解 |
| null 处理 | 默认序列化 null | 默认忽略 null | 需配置 @JsonInclude |
| 泛型支持 | 好(TypeToken) | 好(Types.newParameterizedType) | 好(TypeReference) |
| 多态反序列化 | 需自定义 RuntimeTypeAdapterFactory | 原生支持 @JsonClass(generateAdapter = false) + JsonAdapter | 原生支持 @JsonTypeInfo |
| Kotlin 兼容 | 需额外 kotlin-reflect | 原生最佳(Square 出品) | 需 jackson-module-kotlin |
| 性能 | 一般(大量反射) | 较快(适配器缓存) | 最快(流式解析 + 字节码生成) |
| JAR 大小 | ~230KB | ~150KB | ~1.2MB(含所有模块) |
| 配置难度 | 简单 | 中等 | 较复杂(需管理 ObjectMapper) |
4.3 配置示例
4.3.1 GsonConverter
添加依赖(Gradle):
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
构建 Retrofit 实例:
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd HH:mm:ss")
.setLenient()
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
4.3.2 MoshiConverter
implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
implementation 'com.squareup.moshi:moshi-kotlin:1.14.0'
Moshi moshi = new Moshi.Builder()
.add(new KotlinJsonAdapterFactory())
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(MoshiConverterFactory.create(moshi))
.build();
4.3.3 JacksonConverter
implementation 'com.squareup.retrofit2:converter-jackson:2.9.0'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2'
implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.15.2' // Kotlin 必须
ObjectMapper mapper = new ObjectMapper()
.registerModule(new KotlinModule())
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(JacksonConverterFactory.create(mapper))
.build();
4.4 自定义数据解析器实现
假设服务端返回一种加密的 ProtoBuf 格式,需先解密再反序列化。我们可以自定义 Converter.Factory 来处理。下图展示了自定义工厂在 Retrofit 数据流中的位置:
设计原则: Converter 工厂不应处理业务逻辑(如解密密钥),它只负责“字节流 ↔ 对象”的转换。通常会在工厂外包装一层拦截器做解密,但为了演示完整流程,此处将解密逻辑注入转换器内部。
4.4.1 实现步骤
- 定义数据模型 及 ProtoBuf 生成类(此处略)。
- 实现转换器类:实现
Converter<ResponseBody, MyModel>和Converter<MyModel, RequestBody>。 - 实现 Factory 类:继承
Converter.Factory,在responseBodyConverter()中判断类型是否匹配,返回自定义转换器。 - 注册到 Retrofit:
.addConverterFactory(DecryptConverterFactory.create(secretKey))。
4.4.2 核心代码
public class DecryptConverterFactory extends Converter.Factory {
private final byte[] secretKey;
public static DecryptConverterFactory create(byte[] secretKey) {
return new DecryptConverterFactory(secretKey);
}
private DecryptConverterFactory(byte[] secretKey) {
this.secretKey = secretKey;
}
@Override
public @Nullable Converter<ResponseBody, ?> responseBodyConverter(
Type type, Annotation[] annotations, Retrofit retrofit) {
// 仅处理 MyModel 类型
if (type == MyModel.class) {
return (Converter<ResponseBody, MyModel>) value -> {
// 1. 读取原始字节
byte[] encrypted = value.bytes();
// 2. 解密(例如 AES)
byte[] decrypted = CryptoUtils.aesDecrypt(encrypted, secretKey);
// 3. 使用 ProtoBuf 反序列化
return MyModel.parseFrom(decrypted);
};
}
return null; // 让其他工厂处理
}
@Override
public @Nullable Converter<?, RequestBody> requestBodyConverter(
Type type, Annotation[] parameterAnnotations,
Annotation[] methodAnnotations, Retrofit retrofit) {
if (type == MyModel.class) {
return (Converter<MyModel, RequestBody>) value -> {
byte[] raw = value.toByteArray();
byte[] encrypted = CryptoUtils.aesEncrypt(raw, secretKey);
return RequestBody.create(MediaType.parse("application/x-protobuf"), encrypted);
};
}
return null;
}
}
常见错误:
- 类型判断过于宽泛:
responseBodyConverter中不要对所有type返回非空,否则会覆盖其他工厂;应只对特定模型返回自定义转换器。 - 未处理泛型参数:若自定义转换器需支持泛型,应使用
TypeToken或Types.newParameterizedType()做精确匹配。 - 加密/解密在转换器中:加密逻辑应抽离为独立模块,转换器只负责格式适配,保持单一职责。
4.5 调试与验证
- 启用 OkHttp 日志拦截器
HttpLoggingInterceptor,观察请求/响应的 Content-Type 和原始字节流。 - 在自定义转换器中添加断点或日志,确认
convert()是否被调用。 - 使用
@Headers("Accept: application/json")强制指定 MIME 类型,验证工厂是否按注解路由。
4.6 性能与稳定性优化
| 优化点 | 做法 | 效果 |
|---|---|---|
| 池化 ObjectMapper / Moshi 实例 | 全局单例,避免重复创建 | 减少对象分配,提高吞吐量 |
| 启用转换器缓存 | 在 Factory 内使用 ConcurrentHashMap 缓存 Type→Converter | 避免每次请求重复查找 |
| 避免在转换器中做耗时操作 | 将解密/压缩等操作移至 OkHttp 拦截器或自定义 CallAdapter | 保持转换器轻量,防止阻塞 |
| 支持 Kotlin 协程 | 自定义转换器内不阻塞线程;配合 suspend 函数使用 | 避免线程浪费,提高并发 |
| 错误处理 | 转换器内捕获所有异常并包装为 IOException | 确保 Retrofit 能将错误回调到 Cancellable 或 Callback.onFailure() |
选型建议:
- 纯 Java 项目且简单场景 → Gson(配置少,生态成熟)。
- Kotlin 项目追求零反射 / 严格 null 安全 → Moshi(Square 嫡系,代码生成友好)。
- 高性能 + 复杂多态 + 自定义序列化 → Jackson(内置流式解析,功能最全)。
- 非 JSON 协议(Protobuf、XML、加密流、MessagePack)→ 自定义工厂。
4.7 常见错误清单
- No converter found for class:未注册对应工厂,或工厂
responseBodyConverter返回null。 - ClassCastException:自定义转换器中类型判断错误,返回了不匹配的 Converter。
- Content-Type 不匹配:Retrofit 默认仅按返回的 media type 选择工厂;若服务端返回
text/plain但内容是 JSON,需通过@Headers或自定义拦截器覆盖。 - 内存泄漏:Converter 中持有 Activity / Context 引用(禁止)。