欢迎来到DIVCSS5查找CSS资料与学习DIV CSS布局技术!
  1、在一个.proto文件中定义一个信息格式.
 
  2、使用protoc命令进行编译,生成java代码.
 
  3、使用Java protocol buffer API进行读写操作.
 
  l    定义proto文件
 
  以一个地址薄为例,从建立一个.proto文件开始,为需要序列化的数据接口加入一个message属性,在message里面,为每一个字段指定名称和类型,如下所示:
 
  package tutorial;
 
  option java_package="com.example.tutorial";
 
  option java_outer_classname="AddressBookProtos";
 
  message Person {
 
  required string name=1;
 
  required int32 id=2;
 
  optional string email=3;
 
  enum PhoneType {
 
  MOBILE=0;
 
  HOME=1;
 
  WORK=2;
 
  }
 
  message PhoneNumber {
 
  required
 
  string number=1;
 
  optional
 
  PhoneType type=2 [default=HOME];
 
  }
 
  repeated PhoneNumber phone=4;
 
  }
 
  message AddressBook {
 
  repeated Person person=1;
 
  }
 
  正如你所见, c++和Java中message定义的语法类似,下面我们来看看每个部分的意义:
 
  为了避免命名冲突,.proto文件以包声明开始,在java中除了特别指定一个java_package属性,否则包名一般为Java的包。正像上面的
 
  例子,虽然提供了java_package属性,你通常还是应该定义package属性以避免在ProtocolBuffers中命名冲突。包声明以后,
 
  有两个Java属性:java_package和java_outer_classname。java_package表示生成的Java代码的包,如果
 
  没有指定,编译器会根据package属性确定包名。java_outer_classname属性定义生成文件的类名。如果没有指定,会根据文件名进行
 
  转换,如:"my_proto.proto"缺省会使用MyProto作为外部类名。
 
  接下来是定义message属性,一个message是包含了各种类型字段的聚集。有很多标准的变量类型可以使用,包
 
  括:bool,int32,float,double和string。你也可以使用其他的message作为字段类型。正像例子中的Person包含了
 
  PhoneNumber,而AddressBook包含了Persion。甚至可以在message内部定义message,例
 
  如:PhoneNumber就是在Persion里面定义的。你还可以定义enum类型,正像指定电话号码类型的MOBILE、HOME、WORK。
 
  其中“=1”,“=2”表示每个元素的标识号,它会用在二进制编码中对域的标识。标识号1——15由于使用时会比那些高的标识号少一个字节,从最优化
 
  角度考虑,可以将其使用在一些较常用的或repeated元素上,对于16以上的则使用在不常用的或optional的元素上。对于repeated的每
 
  个元素都需要重复编码该标识号,所以repeated的域进行优化来说是最显示的。
 
  每个字段必须提供一个修饰词:
 
  ?
 
  required:表示字段必须提供,不能为空。否则message会被认为是未初始化的,试图build未初始化的message会抛出
 
  RuntimeException。解析未初始化的message会抛出IOException。除此之外,一个required字段与optional
 
  字段完全相同。
 
  ?
 
  optional:可选字段,可以设置也可以不设置。如果没有设置,会设置一个缺省值。可以指定一个缺省值,正像电话号码的type字段。否则,使用系统
 
  的缺省值:数字类型缺省为0;字符类型缺省为空串;逻辑类型缺省为false;对于嵌入的message,缺省值通常是message的实例或原型。
 
  ?
 
  repeated:字段可以被重复(包括0),可等同于动态数组或列表。其中存储的值列表的顺序是被保留的。
 
  Required修饰的字段是永久性的,在使用该修饰符时一定要特别小心。如果在以后想要修改required域为optional域时会出现问
 
  题。对于访问旧接口的用户来说没有该字段时,将会认为是不合法的访问,将会被拒绝或
 
  更多来源:
 
  丢弃。其中google的一些工程师给出的建议是如果不是必须,就尽量 少用required修饰符。
 
  l    编译Protocol
 
  Buffers文件
 
  既然现在已经有了.proto文件,接下来就需要利用编译器protoc对.proto文件进行编译,生成具体的java类。就可以读取及写入AddressBook、Person及PersonNumber消息了。
 
  protoc -I=$SRC_DIR --java_out=$DST_DIR
 
  $SRC_DIR/addressbook.proto
 
  $SRC_DIR :表示.proto文件所在目录;$DST_DIR:生成的java代码的文件夹。
 
  编译成功后,会在指定的目录下生成Java代码文件,包含了对属性的操作,下一步就可以通过API进行数据的读写了。
 
  l    Protocol Buffer
 
  API使用
 
  接下来具体看一下所生成的java代码及其中的方法。在AddressBookProtos.java中可以看出,其中的内部类对应的是addressbook.proto中定义的格式。每个类都有它自己的Builder类,通过它即可以创建该类的实例。你可以在中查阅到更多关于builder的信息。
 
  Messages和Builders都会为每个域创建自动的访问方法,其中messages只有getters,而builders有getters和setters。下面是Person类message的访问方法:
 
  // required string name=1;
 
  public boolean hasName();
 
  public String getName();
 
  // required int32 id=2;
 
  public boolean hasId();
 
  public int getId();
 
  // optional string email=3;
 
  public boolean hasEmail();
 
  public String getEmail();
 
  // repeated .tutorial.Person.PhoneNumber phone=4;
 
  public List getPhoneList();
 
  public int getPhoneCount();
 
  public PhoneNumber getPhone(int index);
 
  Person类builder的访问方法(Person.Builder):
 
  // required string name=1;
 
  public boolean hasName();
 
  public java.lang.String getName();
 
  public Builder setName(String value);
 
  public Builder clearName();
 
  // required int32 id=2;
 
  public boolean hasId();
 
  public int getId();
 
  public Builder setId(int value);
 
  public Builder clearId();
 
  // optional string email=3;
 
  public boolean hasEmail();
 
  public String getEmail();
 
  public Builder setEmail(String value);
 
  public Builder clearEmail();
 
  // repeated .tutorial.Person.PhoneNumber phone=4;
 
  public List getPhoneList();
 
  public int getPhoneCount();
 
  public PhoneNumber getPhone(int index);
 
  public Builder setPhone(int index, PhoneNumber value);
 
  public Builder addPhone(PhoneNumber value);
 
  public Builder addAllPhone(Iterable value);
 
  public Builder clearPhone();
 
  正如你所见,对于每个域都有简单的javabean风格的getters和setters。对于具有单一值的类型,有has方法用来表示该值是否有设置。当然也可以通过clear方法来将该字段的值清空。
 
  重复域也有额外的方法,如count方法用来统计当前重复域的大小,getters和setters用于根据索引来获取或设置值。add方法用于将一个新元素添加到重复域中,addAll方法则将一组元素添加到重复域中。
 
  上述示例中访问方法的名称采用了驼峰式命名,对应在.proto文件中采用的是小写字母+下划线的命名。这种转换是由protoc编译器自动完成的,我们只需要按照这种规约定义.proto文件即可。
 
  l    枚举和内部类
 
  生成的代码包含了一个枚举类型PhoneType,它属于Person的内部类:
 
  public static enum PhoneType {
 
  MOBILE(0, 0),
 
  HOME(1, 1),
 
  WORK(2, 2),
 
  ;
 
  ...
 
  }
 
  PhoneNumber也是作为Person的一个内部类而产生的。
 
  l    Builders
 
  对Messages
 
  由编译器自动生成的message类是不可变的,一旦一个message对象构建以后,就象java中的String类一样是不可变的。创建一个message时,必须首先创建一个builder,设置必须的一些值后,再调用builder的build()方法。
 
  也许你已经注意到了,builder的每个方法在消息修改后又会返回builder,这个返回对象又可以调用其它方法。这种方式对于在同一行操作不同的方法提供了便利。如下的代码示例,创建一个Person实例。
 
  Person john=
 
  Person.newBuilder()
 
  .setId(1234)
 
  .setName("John Doe")
 
  .setEmail("jdoe@example.com")
 
  .addPhone(
 
  Person.PhoneNumber.newBuilder()
 
  .setNumber("555-4321")
 
  .setType(Person.PhoneType.HOME))
 
  .build();
 
  l    标准的Message方法
 
  对于每个message或builder类也包含一些方法用于检查或操作整个消息,如:
 
  ·                isInitialized():检查是否所有的required字段已经设置了值;
 
  ·                toString():返回一个易于阅读的消息结果,对于调试来说非常有用;
 
  ·                mergeFrom(Message
 
  other): 将其它内部merger到当前的消息中,重写单一值域或者新增repeated域,仅用于builder。
 
  ·                clear():将所有域清空设置,仅用于builder。

如需转载,请注明文章出处和来源网址:http://www.divcss5.com/html/h60190.shtml