wire, Android和Java的简洁轻量级的协议缓冲器

分享于 

14分钟阅读

GitHub

  繁體 雙語
Clean, lightweight protocol buffers for Android.
  • 源代码名称:wire
  • 源代码网址:http://www.github.com/square/wire
  • wire源代码文档
  • wire源代码下载
  • Git URL:
    git://www.github.com/square/wire.git
    Git Clone代码到本地:
    git clone http://www.github.com/square/wire
    Subversion代码到本地:
    $ svn co --depth empty http://www.github.com/square/wire
    Checked out revision 1.
    $ cd repo
    $ svn up trunk
    
    有线协议缓冲器

    "一个人必须有密码" - Omar !

    随着我们团队和程序的增长,数据的种类和数量也在增长。 成功将把简单的数据模型变成复杂的 ! 应用程序是否将数据存储到磁盘或者通过网络传输,该数据的结构和解释应该清楚。 消费者最好使用他们所理解的数据 !

    模式描述和文档模型。 如果有数据,则应该有模式。

    协议缓冲区

    google buffer的协议是围绕一个伟大的模式语言构建的:

    • 它是跨平台和语言独立。 无论使用何种编程语言,你都可以在应用程序中使用原始模式。

    • 原始模式是向后兼容和未来证明。 你可以在应用程序失去旧特性并获得新特性时发展你的模式。

    • 它是重点。原始模式描述你的数据模型。 完成。

    下面是一个示例消息定义:

    syntax ="proto2";packagesquareup.dinosaurs;optionjava_package = "com.squareup.dinosaurs";import"squareup/geology/period.proto";messageDinosaur {
     //Common name of this dinosaur, like"Stegosaurus".optionalstringname = 1;
     //URLs with images of this dinosaur.repeatedstringpicture_urls = 2;
     optionalsquareup.geology.Periodperiod = 5;
    }

    下面是一个 enum 定义:

    syntax ="proto2";packagesquareup.geology;optionjava_package = "com.squareup.geology";enumPeriod {
     //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;
    }

    这里模式语言是协议缓冲区的最佳功能。 甚至可以将它纯粹用于文档目的,比如描述 JSON API。

    协议缓冲区还定义符合架构的消息的compact 二进制编码。 编码快速编码,快速解码,小到传输,小型存储。 二进制编码使用模式中的数字标记,如上面的period5

    例如让我们对这个恐龙进行编码:

    
    {
    
    
     name:"Stegosaurus",
    
    
     period: JURASSIC
    
    
    }
    
    
    
    

    编码的值只有 15字节:

    
    Hex Description
    
    
     0a tag: name(1), field encoding: LENGTH_DELIMITED(2). 1 <<3 | 2
    
    
     0b"Stegosaurus".length()
    
    
     53 'S'
    
    
     74 't'
    
    
     65 'e'
    
    
     67 'g'
    
    
     6f 'o'
    
    
     73 's'
    
    
     61 'a'
    
    
     75 'u'
    
    
     72 'r'
    
    
     75 'u'
    
    
     73 's'
    
    
     28 tag: period(5), field encoding: VARINT(0). 5 <<3 | 0
    
    
     02 JURASSIC(2)
    
    
    
    

    为什么电线?

    协议缓冲区和二进制编码都是由Google定义的。 Wire是来自Square的独立实现,专门为Android和Java设计的。

    对于模式中定义的每个消息类型,导线生成一个不可变的模型类及其生成器。 生成的代码看起来像你手工编写的代码: 它有文档化。格式化和简单。 对于那些喜欢 Effective Java的程序员来说,电线的api应该在家里感到

    也就是说,有一些有趣的设计决定:

    • Wire消息声明 public final 字段而不是通常的getter方法。 这将减少生成的代码和执行的代码。 更少的代码对于Android程序尤其有用。

    • 连线避免了实例映射。在模式中声明为 picture_urls的字段产生一个Java字段 picture_urls,而不是常规的pictureUrls camel。 尽管名字开始很难,但是当你使用 grep 或者更复杂的搜索工具时,它。 在模式。Java源代码和数据之间导航时不再进行映射。 它还为调用原始消息的代码提供了一个温和的提醒。

    • 基本类型始终装箱。 如果字段不存在,它的值为 null。 这用于自然可选的字段,比如一个周期未知的恐龙。 字段也可能是空的,因为模式演化: 如果明天我们向消息定义添加一个 carnivore 布尔值,那么今天的数据将没有这个字段的值。

    下面是为上面定义的Dinosaur 消息生成的compact 代码:

    // Code generated by Wire protocol buffer compiler, do not edit.// Source file: squareup/dinosaurs/dinosaur.proto at 9:1packagecom.squareup.dinosaurs;importcom.squareup.geology.Period;importcom.squareup.wire.Message;importcom.squareup.wire.ProtoAdapter;importcom.squareup.wire.WireField;importjava.util.List;importokio.ByteString;publicfinalclassDinosaurextendsMessage<Dinosaur, Dinosaur.Builder> {
     publicstaticfinalProtoAdapter<Dinosaur>ADAPTER=ProtoAdapter.newMessageAdapter(Dinosaur.class);
     privatestaticfinallong serialVersionUID =0L;
     publicstaticfinalStringDEFAULT_NAME="";
     publicstaticfinalPeriodDEFAULT_PERIOD=Period.CRETACEOUS;
     /** * Common name of this dinosaur, like"Stegosaurus".*/@WireField(
     tag=1,
     adapter="com.squareup.wire.ProtoAdapter#STRING" )
     publicfinalString name;
     /** * URLs with images of this dinosaur.*/@WireField(
     tag=2,
     adapter="com.squareup.wire.ProtoAdapter#STRING",
     label=WireField.Label.REPEATED )
     publicfinalList<String> picture_urls;
     @WireField(
     tag=5,
     adapter="com.squareup.geology.Period#ADAPTER" )
     publicfinalPeriod period;
     publicDinosaur(Stringname, List<String>picture_urls, Periodperiod) {
     this(name, picture_urls, period, ByteString.EMPTY);
     }
     publicDinosaur(Stringname, List<String>picture_urls, Periodperiod, ByteStringunknownFields) {
     super(unknownFields);
     this.name = name;
     this.picture_urls = immutableCopyOf("picture_urls", picture_urls);
     this.period = period;
     }
     @OverridepublicBuildernewBuilder() {
     Builder builder =newBuilder();
     builder.name = name;
     builder.picture_urls = copyOf("picture_urls", picture_urls);
     builder.period = period;
     builder.addUnknownFields(unknownFields());
     return builder;
     }
     @Overridepublicbooleanequals(Objectother) {
     if (other ==this) returntrue;
     if (!(other instanceofDinosaur)) returnfalse;
     Dinosaur o = (Dinosaur) other;
     return equals(unknownFields(), o.unknownFields())
     && equals(name, o.name)
     && equals(picture_urls, o.picture_urls)
     && equals(period, o.period);
     }
     @OverridepublicinthashCode() {
     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;
     }
     publicstaticfinalclassBuilderextendscom.squareup.wire.Message.Builder<Dinosaur, Builder> {
     publicString name;
     publicList<String> picture_urls;
     publicPeriod period;
     publicBuilder() {
     picture_urls = newMutableList();
     }
     /** * Common name of this dinosaur, like"Stegosaurus".*/publicBuildername(Stringname) {
     this.name = name;
     returnthis;
     }
     /** * URLs with images of this dinosaur.*/publicBuilderpicture_urls(List<String>picture_urls) {
     checkElementsNotNull(picture_urls);
     this.picture_urls = picture_urls;
     returnthis;
     }
     publicBuilderperiod(Periodperiod) {
     this.period = period;
     returnthis;
     }
     @OverridepublicDinosaurbuild() {
     returnnewDinosaur(name, picture_urls, period, buildUnknownFields());
     }
     }
    }

    创建和访问Prototype模型的Java代码是 compact 和可读的:

    Dinosaur stegosaurus =newDinosaur.Builder()
    . name("Stegosaurus")
    . period(Period.JURASSIC)
    . build();System.out.println("My favorite dinosaur existed in the "+ stegosaurus.period +" period.");

    每个类型都有一个对应的ProtoAdapter,它可以将消息编码为字节,并将字节解码回消息。

    Dinosaur stegosaurus =...byte[] stegosaurusBytes =Dinosaur.ADAPTER.encode(stegosaurus);byte[] tyrannosaurusBytes =...Dinosaur tyrannosaurus =Dinosaur.ADAPTER.decode(tyrannosaurusBytes);

    访问字段时,使用 Wire.get() 将空值替换为相应的默认值:

    Period period =Wire.get(stegosaurus.period, Dinosaur.DEFAULT_PERIOD);

    这与下面的内容等效:

    
    Period period = stegosaurus.period!= null? stegosaurus.period : Dinosaur.DEFAULT_PERIOD;
    
    
    
    

    用导线生成代码

    通过 Maven 插件可以获得导线的编译器。 将 .proto 源放在项目目录的src/main/proto 中,然后使用插件生成 .java 文件。 插件会自动将生成的Java代码添加到你的项目根代码。

    <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 文件。

    编译器可以选择性地将模式修剪为 root 类型的子集及其传递依赖关系。 在项目之间共享架构时,这很有用: Java服务和 Android 应用 都可以使用较大共享模式的子集。

    如果不使用 Maven,则编译器还具有 命令行 接口。 替换 wire-compiler-VERSION-jar-with-dependencies.jar 你的jar的路径。 下载最新的预编译 jar。

    
    % 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
    
    
    
    

    向编译器提供 --android 标志会导致线程消息实现 Parcelable

    如果你使用混淆器,那么你需要添加 keep 规则。 最简单的选择是告诉混淆器不要触摸Wire运行时库和你生成的协议缓冲区( 当然这些简单的规则将错过收缩和优化代码的机会):

    
    -keep class com.squareup.wire.** { *; }
    
    
    -keep class com.yourcompany.yourgeneratedcode.** { *; }
    
    
    
    

    电线

    wire-runtime 包包含运行时支持库,这些库必须包含在使用生成的代码的应用程序中。

    使用 Maven:

    <dependency>
     <groupId>com.squareup.wire</groupId>
     <artifactId>wire-runtime</artifactId>
     <version>2.2.0</version>
    </dependency>

    使用 Gradle:

    compile 'com.squareup.wire:wire-runtime:2.2.0'

    开发版本的快照在sonatype存储库的snapshots 可用。

    不支持

    电线不支持:

    • 组- 解析二进制输入数据时跳过它们

    有线支持消息和字段的自定义选项。 忽略其他自定义选项。 将 --excludes=google.protobuf.* 传递给编译器以省略生成代码中的选项。

    进一步的文档

    关于Prototype架构的结构和语法,请参见的优秀文档


    JAVA  Light  proto  protocol  protoc  buffers  
    相关文章