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 工厂对比

特性GsonConverterFactoryMoshiConverterFactoryJacksonConverterFactory
底层库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 工厂不应处理业务逻辑(如解密密钥),它只负责“字节流 ↔ 对象”的转换。通常会在工厂外包装一层拦截器做解密,但为了演示完整流程,此处将解密逻辑注入转换器内部。
自定义 Converter 工厂在 Retrofit 中的工作流 客户端 Api Retrofit 内核 序列化/反序列化 Converter.Factory 链 Gson / Moshi / Jackson / 自定义 自定义 DecryptConverterFactory responseBodyConverter → 解密 → 反序列化 requestBodyConverter → 序列化 → 加密 注册顺序决定优先级 1. 自定义工厂通常放在最后, 作为兜底方案。 2. 若同一类型可被多个工厂 处理,先注册者优先。

4.4.1 实现步骤

  1. 定义数据模型 及 ProtoBuf 生成类(此处略)。
  2. 实现转换器类:实现 Converter<ResponseBody, MyModel> 和 Converter<MyModel, RequestBody>。
  3. 实现 Factory 类:继承 Converter.Factory,在 responseBodyConverter() 中判断类型是否匹配,返回自定义转换器。
  4. 注册到 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 引用(禁止)。