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

资讯详情

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

Retrofit 官方 Wire Converter 使用与源码解析:基于 Protocol Buffers 的类型安全 HTTP 客户端序列化方案

Retrofit 官方 Wire Converter 使用与源码解析:基于 Protocol Buffers 的类型安全 HTTP 客户端序列化方案 Retrofit 官方 Wire Converter 使用与源码解析基于 Protocol Buffers 的类型安全 HTTP 客户端序列化方案【免费下载链接】retrofitA type-safe HTTP client for Android and the JVM项目地址: https://gitcode.com/gh_mirrors/re/retrofit导读本文围绕 Retrofit 官方提供的converter-wire转换器展开讲解如何借助 Wire 为 Retrofit 接入 Protocol Buffersprotobuf兼容的请求/响应序列化能力。你将掌握该转换器的 Maven/Gradle 依赖引入方式、Retrofit.Builder中的注册方法、Message子类的类型匹配规则以及create()与withStreaming()两种请求序列化模式的区别并通过仓库源码与测试用例深入理解其底层工作原理最终能够在实际 Android/JVM 项目中快速落地 protobuf 通信。一、Wire Converter 是什么Wire 转换器是 Retrofit 官方发布的一款Converter实现其核心职责是使用 Wire 库完成 Protocol Buffers 兼容的序列化与反序列化。它属于 retrofit-converters 模块家族中的一员。Retrofit 本身只内置支持 OkHttp 的RequestBody与ResponseBody类型对内容格式JSON、XML、protobuf 等完全无感content-format agnostic。retrofit-converters下的各个子模块正是为其他流行数据格式提供的附加转换器converter-wire就是其中之一专门面向 protobuf。在源码层面retrofit2.converter.wire包共包含 5 个文件见 retrofit-converters/wire/src/main/java/retrofit2/converter/wireWireConverterFactory对外暴露的工厂入口负责按类型分派转换器WireRequestBodyConverter把请求消息编码为RequestBodyWireResponseBodyConverter把响应体解码为消息实例WireStreamingRequestBody流式请求体仅withStreaming()模式下使用package-info.java包级空安全注解声明。二、依赖引入根据 converter-wire 的 README可通过 Maven 或 Gradle 引入依赖将latest.version替换为你实际使用的版本号当前仓库为 Retrofit 2.x 系列dependency groupIdcom.squareup.retrofit2/groupId artifactIdconverter-wire/artifactId versionlatest.version/version /dependency或使用 Gradleimplementation com.squareup.retrofit2:converter-wire:latest.version除此之外你还需要引入 Wire 运行时库com.squareup.wire:wire-runtime以提供Message、ProtoAdapter等核心类以及由 Wire 编译器根据.proto文件生成的 protobuf 消息类。开发版本的快照snapshot可以在 Sonatype 的snapshots仓库中获取。三、在 Retrofit 中注册并使用3.1 注册转换器工厂参照 retrofit-converters/README.md 中通用的转换器注册方式构建Retrofit实例时通过addConverterFactory传入WireConverterFactoryRetrofit retrofit new Retrofit.Builder() .baseUrl(https://api.example.com) .addConverterFactory(WireConverterFactory.create()) .build();3.2 定义 Service 接口工厂创建后只需在接口方法中使用 Wire 生成的消息类型作为参数与返回值即可自动完成 protobuf 编解码。仓库测试 WireConverterFactoryTest.java 中给出了完整示例interface Service { GET(/) CallPhone get(); POST(/) CallPhone post(Body Phone impl); POST(/) CallVoid postCrashing(Body CrashingPhone impl); GET(/) CallString wrongClass(); GET(/) CallListString wrongType(); }测试中使用的Phone是 Wire 编译器生成的典型消息类见 Phone.java它继承自com.squareup.wire.Message字段通过WireField(tag 1, adapter com.squareup.wire.ProtoAdapter#STRING)声明并附带一个公开的ProtoAdapterPhone ADAPTER常量——这正是 Wire Converter 编解码的基石。3.3 一个完整的收发流程仓库测试serializeAndDeserialize演示了请求序列化与响应反序列化的完整闭环ByteString encoded ByteString.decodeBase64(Cg4oNTE5KSA4NjctNTMwOQ); server.enqueue(new MockResponse().setBody(new Buffer().write(encoded))); CallPhone call service.post(new Phone((519) 867-5309)); ResponsePhone response call.execute(); Phone body response.body(); assertThat(body.number).isEqualTo((519) 867-5309); RecordedRequest request server.takeRequest(); assertThat(request.getBody().readByteString()).isEqualTo(encoded); assertThat(request.getHeader(Content-Type)).isEqualTo(application/x-protobuf);该测试同时验证了两个关键事实发出的请求体与预编码的 protobuf 字节完全一致请求的Content-Type头固定为application/x-protobuf在 WireRequestBodyConverter.java 中通过MediaType.get(application/x-protobuf)定义。四、源码剖析WireConverterFactory 的类型匹配规则WireConverterFactory.java 继承自Converter.Factory实现了两个核心方法4.1 响应转换器responseBodyConverterOverride public Nullable ConverterResponseBody, ? responseBodyConverter( Type type, Annotation[] annotations, Retrofit retrofit) { if (!(type instanceof Class?)) { return null; } Class? c (Class?) type; if (!Message.class.isAssignableFrom(c)) { return null; } ProtoAdapter? extends Message adapter ProtoAdapter.get((Class? extends Message) c); return new WireResponseBodyConverter(adapter); }4.2 请求转换器requestBodyConverterOverride public Nullable Converter?, RequestBody requestBodyConverter( Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) { if (!(type instanceof Class?)) { return null; } Class? c (Class?) type; if (!Message.class.isAssignableFrom(c)) { return null; } ProtoAdapter? extends Message adapter ProtoAdapter.get((Class? extends Message) c); return new WireRequestBodyConverter(adapter, streaming); }类型匹配规则可以概括为两点必须是Class类型泛型类型如ListPhone会直接返回null交由工厂链中的下一个转换器处理。测试deserializeWrongType验证了这一点——当返回类型为ListString时Retrofit 会抛出IllegalArgumentException其 cause 明确列出尝试过的转换器清单BuiltInConverters、WireConverterFactory、OptionalConverterFactory必须是Message的子类即由 Wire 编译器生成的消息类型。测试deserializeWrongClass中当方法返回String时同样抛出IllegalArgumentException错误信息为Could not locate ResponseBody converter for class java.lang.String。满足条件后工厂通过ProtoAdapter.get(Class)获取消息类自带的适配器然后包装成具体的转换器返回。这意味着该转换器只对Message子类生效其他类型一律不接管——这正是 Retrofit 转换器链设计的精髓每个工厂各司其职互不干扰。五、两种请求序列化模式create() 与 withStreaming()WireConverterFactory提供了两个工厂方法对应两种截然不同的序列化时机5.1create()调用线程上的即时eager编码public static WireConverterFactory create() { return new WireConverterFactory(false); }其 javadoc 明确说明请求消息会在调用线程上即时编码为字节。所谓调用线程对Call.execute()而言是当前调用线程对Call.enqueue()而言则是调用enqueue的线程注意请求字节的编码并不在 OkHttp 后台线程完成。对应的实现见 WireRequestBodyConverter.javaOverride public RequestBody convert(T value) throws IOException { if (streaming) { return new WireStreamingRequestBody(adapter, value); } Buffer buffer new Buffer(); adapter.encode(buffer, value); return RequestBody.create(MEDIA_TYPE, buffer.snapshot()); }非流式模式下消息在convert调用时就被adapter.encode写入Buffer并快照成字节数组整个过程是同步、内存驻留的。5.2withStreaming()HTTP 线程上的流式编码public WireConverterFactory withStreaming() { return new WireConverterFactory(true); }流式模式下序列化被推迟到 OkHttp 真正写请求体时执行发生在 HTTP 线程上execute()为调用线程enqueue()为 OkHttp 后台线程见 WireStreamingRequestBody.javafinal class WireStreamingRequestBodyT extends MessageT, ? extends RequestBody { private final ProtoAdapterT adapter; private final T value; Override public MediaType contentType() { return MEDIA_TYPE; } Override public void writeTo(BufferedSink sink) throws IOException { adapter.encode(sink, value); } }两种模式的取舍模式序列化时机序列化线程内存行为create()请求发起时立即编码调用线程完整字节驻留内存buffer.snapshot()withStreaming()写请求体时才编码HTTP 线程边写边编无需整体快照适用建议常规小消息用默认的create()即可简单直观如果请求体体积较大、希望降低内存峰值可以选择withStreaming()但要注意序列化错误会推迟到网络写入阶段才暴露并被封装为IOException。测试 WireConverterFactoryTest.java 中的serializeIsStreamed用例专门验证了这一行为它构造一个CrashingPhone其ProtoAdapter.encode会抛出EOFException(oops!)在流式模式下调用enqueue异常不会在调用线程同步抛出否则说明流式被破坏而是异步地通过onFailure回调暴露。该测试通过TestParameter boolean streaming参数化运行同时覆盖两种模式。六、响应反序列化的实现细节无论请求端采用哪种模式响应端的处理路径是统一的。WireResponseBodyConverter.java 负责把响应字节流解码为消息实例final class WireResponseBodyConverterT extends MessageT, ? implements ConverterResponseBody, T { private final ProtoAdapterT adapter; Override public T convert(ResponseBody value) throws IOException { try { return adapter.decode(value.source()); } finally { value.close(); } } }实现要点响应字节始终在 OkHttp 后台线程解码WireConverterFactory的 javadoc 明确说明不会阻塞主线程解码通过adapter.decode(value.source())直接从 OkHttp 响应体的BufferedSource流式读取避免额外拷贝finally块确保无论解码成功与否ResponseBody都会被关闭避免资源泄漏。Phone.ADAPTER生成的解码器Phone.java 中的ProtoAdapter_Phone.decode会逐字段解析 tag遇到未知字段时通过builder.addUnknownField(...)保留protobuf 的前向/后向兼容机制并在redact时清除。仓库测试还覆盖了若干边界场景deserializeEmpty空响应体也能正常解码为Phone字段值为null对应Phone.DEFAULT_NUMBER之外的状态deserializeWrongValue非法字节如////会抛出EOFExceptiondeserializeWrongClass/deserializeWrongType非Message类型在构建 Service 时即抛出带完整转换器尝试链的IllegalArgumentException便于快速定位问题。七、使用建议与注意事项确保.proto文件已生成对应消息类converter-wire只负责编解码消息类必须由 Wire 编译器生成并继承Message、持有ProtoAdapter常量如Phone.ADAPTER接口返回类型与参数类型必须是具体消息类不要使用ListPhone、接口类型或抽象类型作为转换目标工厂会拒绝处理并抛错Content-Type 由转换器自动设置请求头Content-Type: application/x-protobuf由 WireRequestBodyConverter.java 中定义的MEDIA_TYPE常量决定无需手动指定大请求体优先考虑withStreaming()需要权衡提前在调用线程编码、错误及时暴露与延迟到 HTTP 线程流式编码、内存占用更低两种策略多转换器共存addConverterFactory可以链式注册多个工厂如 JSON 转换器与 Wire 转换器共存Retrofit 会按注册顺序依次尝试WireConverterFactory只认领Message子类其余类型自动落到后续工厂空安全包级注解retrofit2.internal.EverythingIsNonNull见 package-info.java声明该包所有公开 API 的非空约束配合静态分析工具可提升代码健壮性。八、总结converter-wire是 Retrofit 官方为 Protocol Buffers 提供的标准解决方案通过 WireConverterFactory 一行注册即可让 Retrofit 获得 protobuf 编解码能力内部以 Wire 生成的ProtoAdapter为引擎请求端支持即时编码与流式编码两种模式响应端统一在 OkHttp 后台线程流式解码并保证资源释放。配合 WireConverterFactoryTest.java 中覆盖的正反用例你可以清晰地理解其类型匹配边界、线程模型与错误语义进而在项目中安全、高效地使用 protobuf 通信。【免费下载链接】retrofitA type-safe HTTP client for Android and the JVM项目地址: https://gitcode.com/gh_mirrors/re/retrofit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表