Wire:整洁的轻量级协议缓存
Wire是一个简洁的轻量级协议缓存,针对Android与Java平台。Wire与Square无关,是完全独立的缓存实现,特别针对Android与Java平台设计。
功能特性
对schema中定义的每种消息类型,Wire都会生成一个不可变(immutable)模型类及builder。生成的代码质量与手写一致,代码包含文档说明、已排版并且非常简洁。如果你喜欢Effective Java,相信Wire的API会让你感到非常亲切。
也就是说,Wire的设计包含了以下特点:
- Wire消息声明:放弃通常的getter方法,使用
public final
字段,简化了代码生成与代码执行。减少代码量在Android应用中的好处尤其明显。 - Wire避免了大小写映射:在schema中定义的
picture_urls
字段,会对应生成名为picture_urls
的Java字段,而非传统的pictureUrls
驼峰式命名。尽管名称开始看起来有些怪,对于grep
或者其他复杂的搜索工具来说却很方便。这种做法不再需要schema、Java源代码、数据之间的映射。不仅如此,在调用特殊的消息时还能起到友好的提示。 - 基础类型装箱:如果缺少某个字段,它的值
null
,适用于非必须的字段。例如恐龙锁处的时间代未知。在schema升级的过程中字段也可能变为null:如果明天再消息中增加一个carnivore
的boolean字段,那么今天的数据中则会缺少这个字段。
下载
wire-runtime
包含了使用Wire生成代码的应用程序运行时必须包含的支持库:
Maven
1 2 3 4 5 |
<dependency> <groupId>com.squareup.wire</groupId> <artifactId>wire-runtime</artifactId> <version>2.2.0</version> </dependency> |
Gradle
1 |
compile 'com.squareup.wire:wire-runtime:2.2.0' |
开发版本的镜像可以从Sonatype snapshots仓库下载。
使用示例
定义Dinosaur消息
下面是一个消息定义示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
syntax = "proto2"; package squareup.dinosaurs; option java_package = "com.squareup.dinosaurs"; import "squareup/geology/period.proto"; message Dinosaur { // Common name of this dinosaur, like "Stegosaurus". optional string name = 1; // URLs with images of this dinosaur. repeated string picture_urls = 2; optional squareup.geology.Period period = 5; } |
这是一个enum定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
syntax = "proto2"; package squareup.geology; option java_package = "com.squareup.geology"; enum Period { // 145.5 million years ago — 66.0 million years ago. CRETACEOUS = 1; // 201.3 million years ago — 145.0 million years ago. JURASSIC = 2; // 252.17 million years ago — 201.3 million years ago. TRIASSIC = 3; } |
生成的Java代码
下面是Dinosaur
消息生成的一个紧凑的定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
// Code generated by Wire protocol buffer compiler, do not edit. // Source file: squareup/dinosaurs/dinosaur.proto at 9:1 package com.squareup.dinosaurs; import com.squareup.geology.Period; import com.squareup.wire.Message; import com.squareup.wire.ProtoAdapter; import com.squareup.wire.WireField; import java.util.List; import okio.ByteString; public final class Dinosaur extends Message<Dinosaur, Dinosaur.Builder> { public static final ProtoAdapter<Dinosaur> ADAPTER = ProtoAdapter.newMessageAdapter(Dinosaur.class); private static final long serialVersionUID = 0L; public static final String DEFAULT_NAME = ""; public static final Period DEFAULT_PERIOD = Period.CRETACEOUS; /** * Common name of this dinosaur, like "Stegosaurus". */ @WireField( tag = 1, adapter = "com.squareup.wire.ProtoAdapter#STRING" ) public final String name; /** * URLs with images of this dinosaur. */ @WireField( tag = 2, adapter = "com.squareup.wire.ProtoAdapter#STRING", label = WireField.Label.REPEATED ) public final List<String> picture_urls; @WireField( tag = 5, adapter = "com.squareup.geology.Period#ADAPTER" ) public final Period period; public Dinosaur(String name, List<String> picture_urls, Period period) { this(name, picture_urls, period, ByteString.EMPTY); } public Dinosaur(String name, List<String> picture_urls, Period period, ByteString unknownFields) { super(unknownFields); this.name = name; this.picture_urls = immutableCopyOf("picture_urls", picture_urls); this.period = period; } @Override public Builder newBuilder() { Builder builder = new Builder(); builder.name = name; builder.picture_urls = copyOf("picture_urls", picture_urls); builder.period = period; builder.addUnknownFields(unknownFields()); return builder; } @Override public boolean equals(Object other) { if (other == this) return true; if (!(other instanceof Dinosaur)) return false; Dinosaur o = (Dinosaur) other; return equals(unknownFields(), o.unknownFields()) && equals(name, o.name) && equals(picture_urls, o.picture_urls) && equals(period, o.period); } @Override public int hashCode() { int result = super.hashCode; if (result == 0) { result = unknownFields().hashCode(); result = result * 37 + (name != null ? name.hashCode() : 0); result = result * 37 + (picture_urls != null ? picture_urls.hashCode() : 1); result = result * 37 + (period != null ? period.hashCode() : 0); super.hashCode = result; } return result; } public static final class Builder extends com.squareup.wire.Message.Builder<Dinosaur, Builder> { public String name; public List<String> picture_urls; public Period period; public Builder() { picture_urls = newMutableList(); } /** * Common name of this dinosaur, like "Stegosaurus". */ public Builder name(String name) { this.name = name; return this; } /** * URLs with images of this dinosaur. */ public Builder picture_urls(List<String> picture_urls) { checkElementsNotNull(picture_urls); this.picture_urls = picture_urls; return this; } public Builder period(Period period) { this.period = period; return this; } @Override public Dinosaur build() { return new Dinosaur(name, picture_urls, period, buildUnknownFields()); } } } |
创建proto模型访问的Java代码非常简洁,可读性很好:
1 2 3 4 5 6 |
Dinosaur stegosaurus = new Dinosaur.Builder() .name("Stegosaurus") .period(Period.JURASSIC) .build(); System.out.println("My favorite dinosaur existed in the " + stegosaurus.period + " period."); |
每个类型都有一个对应的ProtoAdapter
,可以把消息编码为字节并把字节解码为消息。
1 2 3 4 5 |
Dinosaur stegosaurus = ... byte[] stegosaurusBytes = Dinosaur.ADAPTER.encode(stegosaurus); byte[] tyrannosaurusBytes = ... Dinosaur tyrannosaurus = Dinosaur.ADAPTER.decode(tyrannosaurusBytes); |
访问字段时,使用Wire.get()
用默认值替换null:
1 |
Period period = Wire.get(stegosaurus.period, Dinosaur.DEFAULT_PERIOD); |
上面的操作与下面等价:
1 |
Period period = stegosaurus.period != null ? stegosaurus.period : Dinosaur.DEFAULT_PERIOD; |
生成代码工具
Wire编译器可以通过Maven插件安装。把 .proto
源代码放到项目中 src/main/proto
的目录,通过插件生成 .java
文件。插件会自动把生成的Java代码添加到项目源代码根目录。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<build> <plugins> <plugin> <groupId>com.squareup.wire</groupId> <artifactId>wire-maven-plugin</artifactId> <version>${project.version}</version> <executions> <execution> <phase>generate-sources</phase> <goals> <goal>generate-sources</goal> </goals> <configuration> <includes> <!-- proto package names to generate code for --> <include>squareup.dinosaurs.*</include> <include>squareup.geology.*</include> </includes> </configuration> </execution> </executions> </plugin> </plugins> </build> |
Wire从本地文件与.jar
文件中读取.proto
文件。
编译器会将schema删减为一组根类型(root type)子集,并处理相关的类型。这在项目之前分享schema信息非常有用:Java服务与Android app可以使用共享schema的一组子集。
如果不用Maven,编译器还提供了一组命令行接口。只要将wire-compiler-VERSION-jar-with-dependencies.jar
替换为jar路径即可。
1 2 3 4 5 6 7 8 |
% java -jar wire-compiler-VERSION-jar-with-dependencies.jar \ --proto_path=src/main/proto \ --java_out=out \ squareup/dinosaurs/dinosaur.proto \ squareup/geology/period.proto Writing com.squareup.dinosaurs.Dinosaur to out Writing com.squareup.geology.Period to out |
如果使用Proguard,需要添加keep
规则。最简单的方式是让Proguard不要访问Wire运行时库与生成的协议缓存(当然这些代码也就不会被优化处理):
1 2 |
-keep class com.squareup.wire.** { *; } -keep class com.yourcompany.yourgeneratedcode.** { *; } |
使用许可
Wire遵循Apache 2.0开源协议发布。
打赏支持我整理更多优质资源,谢谢!
打赏编辑
打赏支持我整理更多优质资源,谢谢!
任选一种支付方式