尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Retrofit 2 Moshi Converter 完全指南:converter-moshi 的集成、配置与源码解析

Retrofit 2 Moshi Converter 完全指南:converter-moshi 的集成、配置与源码解析 Retrofit 2 Moshi Converter 完全指南converter-moshi 的集成、配置与源码解析【免费下载链接】retrofitA type-safe HTTP client for Android and the JVM项目地址: https://gitcode.com/gh_mirrors/re/retrofit导读本文围绕 Retrofit 2 官方提供的 Moshi JSON 序列化转换器converter-moshi展开介绍如何将其引入项目、通过MoshiConverterFactory接入 Retrofit 构建类型安全的 HTTP 客户端并深入讲解asLenient()、failOnUnknown()、withNullSerialization()、withStreaming()四个关键配置及其底层实现原理。读完本文你将能够独立完成 Moshi 转换器的配置、调试与二次扩展并理解 Retrofit 转换器Converter机制的工作方式。一、什么是 Moshi Converterconverter-moshi是 Retrofit 2 官方维护的转换器模块之一核心职责是使用 [Moshi] 完成 JSON 的序列化与反序列化。在 Retrofit 的网络调用链路中它负责两件事将接口方法参数如Body注解的对象序列化为okhttp3.RequestBody随 HTTP 请求发出将服务器返回的ResponseBody反序列化为接口方法声明的返回类型。模块的描述信息在 gradle.properties 中定义为POM_NAMEConverter: Moshi、POM_DESCRIPTIONA Retrofit Converter which uses Moshi for serializationArtifactId 为converter-moshi。二、依赖引入Maven在pom.xml中加入如下依赖dependency groupIdcom.squareup.retrofit2/groupId artifactIdconverter-moshi/artifactId versionlatest.version/version /dependencyGradleimplementation com.squareup.retrofit2:converter-moshi:latest.version说明latest.version请替换为实际使用的版本号应与主模块retrofit的版本保持一致。开发版本的快照SNAPSHOT发布在 Sonatype 的snapshots仓库如需体验最新改动可配置该仓库获取。由于converter-moshi依赖 Moshi 与 OkHttp引入后需确保构建环境可解析com.squareup.moshi:moshi与com.squareup.okhttp3:okhttp依赖本仓库的版本统一由 gradle/libs.versions.toml 管理。三、快速接入基本用法MoshiConverterFactory是接入入口提供两个静态工厂方法见 MoshiConverterFactory.javacreate()内部自动构建一个默认的Moshi实例等价于new Moshi.Builder().build()create(Moshi moshi)接收调用方自定义的Moshi实例用于精细控制序列化行为如自定义JsonAdapter、JsonQualifier适配器等参数为null时会抛出NullPointerException(moshi null)。典型接入代码Retrofit retrofit new Retrofit.Builder() .baseUrl(https://api.example.com/) .addConverterFactory(MoshiConverterFactory.create()) .build(); MyService service retrofit.create(MyService.class);接口定义示例public interface GitHubService { GET(users/{user}/repos) CallListRepo listRepos(Path(user) String user); POST(repos) CallRepo createRepo(Body Repo repo); }这里Repo、ListRepo的解析与Body Repo的序列化都由 Moshi 完成测试用例 MoshiConverterFactoryTest.java 验证了该链路请求体被序列化为{name:value}形式且Content-Type为application/json; charsetUTF-8。四、深入配置四个关键方法除基础用法外MoshiConverterFactory提供四个链式方法返回新的工厂实例不可变风格原实例不受影响。方法作用对应 Moshi JsonAdapter 能力asLenient()允许解析宽松格式不严格的 JSONJsonAdapter.lenient()failOnUnknown()遇到 JSON 中未知字段时直接报错JsonAdapter.failOnUnknown()withNullSerialization()将值为null的字段也序列化输出JsonAdapter.serializeNulls()withStreaming()请求体序列化改为流式写入延迟到 HTTP 发送阶段自定义流式RequestBody4.1 asLenient()宽松解析默认情况下 Moshi 是严格模式字段名未加引号、字符串使用单引号、数字带前导零等非标准 JSON 都会抛异常。调用asLenient()后改用宽松模式容忍这类输入。测试用例asLenient()MoshiConverterFactoryTest.java#L254-L274验证响应{theName:value}这种缺引号的 JSON 在严格模式下抛IOException提示 Use JsonReader.setLenient(true) to accept malformed JSON而宽松模式下可正常解析。适用场景对接第三方接口返回的格式不严格 JSON。注意宽松模式也会掩盖部分真实格式问题生产环境应谨慎启用。4.2 failOnUnknown()未知字段即失败默认 Moshi 会静默跳过 JSON 中类未定义的字段。启用failOnUnknown()后遇到未知字段直接抛JsonDataException。测试用例failOnUnknown()MoshiConverterFactoryTest.java#L285-L296验证响应{taco:delicious}而目标类没有taco字段时抛出的异常消息为Cannot skip unexpected NAME at $.taco。适用场景严格校验响应契约防止后端悄悄变更字段导致前端静默出错。4.3 withNullSerialization()输出 null 字段Moshi 默认序列化时省略值为 null 的字段对应 Gson 的serializeNulls行为。启用后 null 字段也会写入 JSON。测试用例withNulls()MoshiConverterFactoryTest.java#L276-L283验证对象字段为null时请求体输出{theName:null}。适用场景服务端要求字段必须显式存在即使为 null或需要向客户端明确表达字段存在但无值。4.4 withStreaming()流式序列化这是 2025 年新增的能力源码版权声明为 2025见 MoshiStreamingRequestBody.java。默认模式下请求体在调用线程上一次性完整序列化到内存缓冲区Buffer后再发送启用流式模式后序列化延迟到 HTTP 请求真正写入网络流writeTo时进行逐字节写入 OkHttp 的BufferedSink。两者的线程模型差异来自 MoshiConverterFactory.java#L93-L101 的文档注释execute()同步调用序列化发生在调用线程enqueue()异步调用序列化发生在 OkHttp 的某个后台线程。适用场景请求体体积大、序列化耗时的场景可避免调用线程被序列化阻塞同步调用时尤其明显。注意序列化异常此时也在后台线程抛出需通过Callback.onFailure捕获——测试用例serializeIsStreamedMoshiConverterFactoryTest.java#L341-L369验证了该行为。组合示例MoshiConverterFactory factory MoshiConverterFactory.create(moshi) .asLenient() .withNullSerialization() .failOnUnknown() .withStreaming();五、源码解析Converter 是如何工作的5.1 工厂类结构MoshiConverterFactory extends Converter.FactoryMoshiConverterFactory.java#L46重写了Converter.Factory的两个钩子方法responseBodyConverter(Type, Annotation[], Retrofit)为响应类型解析出JsonAdapter返回MoshiResponseBodyConverterrequestBodyConverter(Type, Annotation[], Annotation[], Retrofit)为请求体类型解析出JsonAdapter返回MoshiRequestBodyConverter。工厂内部维护moshi、lenient、failOnUnknown、serializeNulls、streaming五个状态字段四个配置方法分别以复制并修改布尔开关的方式返回新实例见 MoshiConverterFactory.java#L78-L101。5.2 JsonQualifier 注解转发工厂在查找JsonAdapter时会调用moshi.adapter(type, jsonAnnotations(annotations))将带有JsonQualifier元注解的自定义注解透传给 MoshiMoshiConverterFactory.java#L138-L147请求体转换器使用参数上的JsonQualifier注解响应体转换器使用方法上的JsonQualifier注解。这允许你通过自定义注解区分同一类型在不同场景下的序列化策略。测试用例annotations()MoshiConverterFactoryTest.java#L241-L252 对应测试位于测试文件 L242验证自定义Qualifier标注JsonQualifier注解可让 String 被序列化为qualified!并反序列化回it worked!而普通NonQualifer注解则被忽略。5.3 响应转换器细节UTF-8 BOM 与文档完整性MoshiResponseBodyConverter.java 的反序列化过程包含两个值得注意的细节跳过 UTF-8 BOMMoshi 没有文档级 APIBOM 处理需由调用方负责。由于它是纯 UTF-8 库本转换器只识别 UTF-8 BOM十六进制EFBBBF命中则跳过L41-L44。测试用例utf8BomSkipped/nonUtf8BomIsNotSkippedMoshiConverterFactoryTest.java#L298-L326分别验证了这两种情况。强制消费完整文档解析完成后检查JsonReader是否到达END_DOCUMENT否则抛出JsonDataException(JSON document was not fully consumed.)L46-L49测试用例requireFullResponseDocumentConsumptionMoshiConverterFactoryTest.java#L328-L339覆盖此场景。5.4 请求转换器细节内存缓冲与流式两种模式MoshiRequestBodyConverter.java 根据streaming开关走两条路径默认模式先序列化到okio.Buffer再包装为RequestBody.create(MEDIA_TYPE, buffer.readByteString())流式模式返回MoshiStreamingRequestBody其writeTo(BufferedSink)直接调用adapter.toJson(sink, value)边序列化边写入网络流。两种模式下的Content-Type均为application/json; charsetUTF-8MoshiRequestBodyConverter.java#L27测试用例中也有断言。六、与其他转换器共存加在最后Moshi 极其灵活几乎可以处理任意类型因此MoshiConverterFactory假定自己可以处理所有类型。这意味着如果你混用 JSON 与其他序列化方案如 Protocol Buffers必须将本工厂最后添加让其他转换器先有机会处理它们的类型MoshiConverterFactory.java#L37-L40 的 Javadoc 明确说明此约定Retrofit retrofit new Retrofit.Builder() .baseUrl(https://api.example.com/) .addConverterFactory(ProtoConverterFactory.create()) // 先加专用转换器 .addConverterFactory(MoshiConverterFactory.create()) // Moshi 放最后兜底 .build();Retrofit 在查找转换器时按注册顺序逐个询问命中即停。仓库示例 AnnotatedConverters.java 展示了更复杂的场景通过AnnotatedConverterFactory按注解路由到不同转换器Moshi、Gson、SimpleXML并将GsonConverterFactory作为默认兜底——演示了指定转换器优先、通用转换器兜底的组合模式。七、实战建议与注意事项版本对齐converter-moshi应与主retrofit模块使用同一版本避免 ABI 不兼容。线程模型取舍大请求体且使用同步execute()时优先考虑withStreaming()但要注意流式模式下序列化异常在后台线程抛出需正确处理回调。严格性平衡failOnUnknown()适合契约严格的内部 API对接不可控的第三方 API 时默认的宽容行为更稳妥。BOM 处理已内置响应带 UTF-8 BOM 时无需自行清洗转换器已处理非 UTF-8 编码的响应不在支持范围内Moshi 本身是 UTF-8 专用库。文档完整性转换器强制要求 JSON 文档被完整消费多余内容会抛JsonDataException这是对响应契约的一种隐含校验。null 语义默认省略 null 字段若服务端对字段存在性敏感用withNullSerialization()显式输出。八、相关资源模块文档retrofit-converters/moshi/README.md核心实现MoshiConverterFactory.java、MoshiRequestBodyConverter.java、MoshiResponseBodyConverter.java、MoshiStreamingRequestBody.java单元测试MoshiConverterFactoryTest.java组合转换器示例AnnotatedConverters.java转换器抽象Converter.java、Retrofit.java【免费下载链接】retrofitA type-safe HTTP client for Android and the JVM项目地址: https://gitcode.com/gh_mirrors/re/retrofit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表