http://code.google.com/p/protobuf/
Protocol Buffers are a way of encoding structured data in an efficient yet extensible format. Google uses Protocol Buffers for almost all of its internal RPC protocols and file formats.
http://code.google.com/intl/zh-CN/apis/protocolbuffers/docs/proto.html#options
1. java_package
(file option): The package you want to use for your generated Java classes.
option java_package = "com.example.foo";
2. java_outer_classname
(file option): The class name for the outermost Java class (and hence the file name) you want to generate.
option java_outer_classname = "Ponycopter";
3. optimize_for
(file option): Can be set to SPEED
, CODE_SIZE
, or LITE_RUNTIME
.
option optimize_for = CODE_SIZE;
http://code.google.com/intl/zh-CN/apis/protocolbuffers/docs/reference/java-generated.html
首先看看下面的proto文件生成的Java文件是什么样子的:
package tutorial; option optimize_for = LITE_RUNTIME; 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; optional bytes addressBytes = 6; } message AddressBook { repeated Person person = 1; }
枚举类型会生成Java中的枚举:
public enum PhoneType implements com.google.protobuf.Internal.EnumLite { MOBILE(0, 0), HOME(1, 1), WORK(2, 2), ; public static final int MOBILE_VALUE = 0; public static final int HOME_VALUE = 1; public static final int WORK_VALUE = 2; ... }
注意同时会生成一个以_VALUE作为后缀的常量。
下面的三个域生成的Java代码是什么样子呢?
required string name = 1;
required int32 id = 2;
optional string email = 3;
首先生成一个接口PersonOrBuilder,有下面的方法:
boolean hasName(); String getName(); boolean hasId(); int getId(); boolean hasEmail(); String getEmail();
然后生成Person类,实现接口PersonOrBuilder,同时,定义了下面的常量:
public static final int NAME_FIELD_NUMBER = 1; public static final int ID_FIELD_NUMBER = 2; public static final int EMAIL_FIELD_NUMBER = 3;
可以看出,都有_FIELD_NUMBER后缀。
可以从isInitialized()函数中看判断某个域是optional还是required。optional的域不会在isInitialized()中进行检查。
public final boolean isInitialized() { if (!hasName()) { return false; } if (!hasId()) { return false; } ... }
对于域是一个message而不是原始类型的情况,判断是optional还是required有些区别:
比如,对于如下的proto:
optional PhoneNumber mobile = 7; required PhoneNumber cell = 8;
对于的Java代码为:
if (!hasCell()) { memoizedIsInitialized = 0; return false; } if (hasMobile()) { if (!getMobile().isInitialized()) { memoizedIsInitialized = 0; return false; } } if (!getCell().isInitialized()) { memoizedIsInitialized = 0; return false; }
对于required message field, Java代码中会首先用一个if语句块hasXxx()判断,然后再用一个if语句块getXxx().isInitialized()进行判断,而对于optional message field,两个if语句块会嵌套一起。
在initFields()函数中查看具体有哪些fields,再结合XXX_FIELD_NUMBER一起分析。
判断extensions:
查看registerAllExtensions()函数。
通常是使用nested extensions,需要好好理解,其实很简单,只是写法有点绕:
http://code.google.com/apis/protocolbuffers/docs/proto.html#extensions
http://code.google.com/apis/protocolbuffers/docs/reference/java-generated.html#extension
下面的例子:
public static final int MAN_FIELD_NUMBER = 123; public static final com.google.protobuf.GeneratedMessageLite.GeneratedExtension< com.example.tutorial.AddressBookProtos.Person, com.example.tutorial.AddressBookProtos.Man> man = com.google.protobuf.GeneratedMessageLite .newSingularGeneratedExtension( com.example.tutorial.AddressBookProtos.Person.getDefaultInstance(), com.example.tutorial.AddressBookProtos.Man.getDefaultInstance(), com.example.tutorial.AddressBookProtos.Man.getDefaultInstance(), null, 123, com.google.protobuf.WireFormat.FieldType.MESSAGE); }
可以得知Man是Person的extension,并且field number是123.
LazyStringList对应 repeat string xxx = 1;
this change coming up in 2.4.0:
"""Added lazy conversion of UTF-8 encoded strings to String objects to improve
performance."""