1 Introduction
Protocol Buffers是在网络通讯或者数据存储时用到的一种语言无关、平台无关、可扩展的序列化结构数据格式。类似于XML,不过比XML更小、更快、更简单。
你首先要定义自己想要的数据结构(在 .proto 文件中定义数据结构信息),经过Protocol Buffers 编译后生成.java代码文件,就可以根据.java里的数据对象来进行读写操作。
下面谈谈在Android开发中如何接入protocol buffers。
2 JavaNano
protocol buffers支持多种语言环境下的使用。Java虽然是Android开发的绝对主流开发语言,但是protobuf还是针对Android开发这种性能资源有限的系统专门提供了一个JavaNano版本(而不是直接使用protobuf的Java版本进行编译),在代码量和运行时开销上都比较友好(详细介绍点这)。
3 Usage
3.1 Defining A Message Type
即编写.protol文件,通常这部分工作由后台完成,毕竟是后端来决定在网络连接中,传输什么字段给客户端或者客户端提交什么字段给服务器。类似于定义json的字段结构。
示例:
syntax = "proto3";//指定protocol buffer版本
message SearchRequest {//这是一个message的定义,包含了三个属性,每个属性有一个字段名和类型
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
message SearchResponse {
...//一些字段的定义
}
3.2 What's Generated From Your .proto?
运行protocol buffer 编译器去编译.proto文件,可以根据你选择的语言生成相应的代码文件。
编译Java语言版本的时候,编译器会为每一个message生成.java
类文件,每个类还对应一个Builder
提供给开发者去构建实例。值得注意的是,我们在Android上使用的protobuf JavaNano与protobuf Java相比,去除了一些复杂的特性。比如刚刚提到的Builder,还有Descriptors都被去掉。同时通过直接访问类的public成员变量的方式取代了set/get方法。
3.3 Compile on Windows
在Windwos上编译.protol文件十分简单。
- protocol 编译器是用C++写的,C++开发者可以在github google/protobuf下载源码编译出来。对于非C++开发者,也可以直接下载现成的编译器protoc-3.0.0-beta-3-win32.zip。(这里获取各个OS下的编译器。)
- 解压下载好的protoc-3.0.0-beta-3-win32.zip,将解压后的目录名添加到Windows的PATH环境变量。
- 调出Windows的命令行,输入
protoc --version
,出现以下画面,说明编译器可以使用了。
protoc --version - 生成Java类
protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/message.proto
上面的$SRC_DIR
表示存放message.proto文件的路径,$DST_DIR
表示生成java类存放的路径。-I=$SRC_DIR
可以省略,如果message.proto文件与protoc.exe在同一个目录下。
例如:
protoc -I=D:\ --javanano_out=E:\ D:\message.proto
这里我的message.proto文件放在D盘,指定编译生成的java类放到E盘。
可以看到编译出来的java类文件已经安静地躺在我的E盘中。这个类就和我们平时使用的Bean类的用法一毛一样了。
3.4 Compile Options
有一些常用的编译选项可以在message.proto文件中预先定义:
option java_package = "com.android.develop"; //指定java类的包名
option java_multiple_files = true; //message.proto文件中的每个message都生成一个对应的.java。设为fasle时只导出一个.java类文件,所有message都以内部类的形式出现
option java_outer_classname = "MessageBean";//当只生成一个.java文件时,指定类名, java_multiple_files =true时,这个属性无效。
message SearchRequest {
int32 page_number = 2;
int32 result_per_page = 3;
}
message OtherMessages{
.......
}
命令行编译时,还有以下选项可以用:
-
optional_field_style={default,accessors,reftypes} (default: default)
设置成员变量的访问方式。default就是public直接访问,accessors就是set/get方法,reftypes使用基本类型的包装类来定义字段类型(例如Interger代替原本的int,空值为null,可以避免空值==0时的歧义。)。 enum_style={c,java} (default: c)
-
parcelable_messages={true,false} (default: false)
开启了这个选项后,编译生成的java类自动实现Parcelable接口,最常用的Intent传值就可以直接使用这些对象。
使用示例:
protoc --javanano_out=optional_field_style=accessors,parcelable_messages=true:d:\ D:\protoc-3.0.0-beta-3-win32\message.proto
这时,编译出来的java类,就是通过set/get方法访问private的成员变量,同时也已经实现了Parcelable接口。
3.5 The Protocol Buffer API
每一个编译后生成的protobuf Message类都提供了方法去读写二进制格式的数据。
-
byte[] toByteArray();
序列化message,并返回一个包含了message的原始数据的byte数组。 -
static SearchRequest parseFrom(byte[] data);
从一个给定的byte数组中,反序列化出来一个message对象。 -
void writeTo(OutputStream output);
序列化message,并写到一个OutputStream
上。 -
static SearchRequest parseFrom(InputStream input);
从一个InputStream
读取并反序列化出message对象。
3.6 Protocol Buffers and O-O Design
官方推荐使用包装类的形式来对生成的ProtoBuf Message java类进行封装及扩展,而不是继承它们。因为继承并重写父类方法的时候将会破坏ProtoBuf Message类的内在机制。
You should never add behaviour to the generated classes by inheriting from them. This will break internal mechanisms and is not good object-oriented practice anyway.