概述
- Protocol Buffers是一种序列化数据结构的方法,Protocol Buffers经常被简称为protobuf。
- 它不依赖于语言和平台并且可扩展性极强。google protobuf是跨语言的,并且自带了一个编译器(protoc),只需要用它进行编译,可以编译成Java、python、C++、C#、Go等代码,然后就可以直接使用,不需要再写其他代码,自带有解析的代码。更详细的介绍见: [Protocol Buffers]
通过它,你可以定义你的数据的结构,并生成基于各种语言的代码。这些你定义的数据流可以轻松地在传递并不破坏你已有的程序。并且你也可以更新这些数据而现有的程序也不会受到任何的影响。
在使用之前,需要下载2个包:
- protobuf (protobuf 在你应用中使用的api包)。protobuf-3.3.0下载链接。
- protoc(编译器,windows版内涵protoc.exe, 用来把proto文件编译成目标语言(C++,Java,Python)的文本。比如目标语言java则声称xxx.java文件)。protoc-3.3.0-win32下载链接
Protocol Buffer Basics: Java
本文主要介绍下在java中使用Protocol Buffer, 通过一个简单的例子应用,将介绍如下内容:
- 如何在 a .proto文件中定义消息格式
- 如何使用 protocol buffer编译器
- 使用 Java protocol buffer API 来读写消息
如果想要理解更多可以参考Protocol Buffer Language Guide、 Java API Reference、 Java Generated Code Guide、 Encoding Reference。
Why Use Protocol Buffers?
“通讯录”应用程序是一个非常简单的例子,它可以读取和写入人们的联系信息。 地址簿中的每个人都有姓名,身份证件,电子邮件地址和联系电话号码。
-如何序列化和检索这样的结构化数据? 有几种方法来解决这个问题:
- 使用Java序列化。 这是默认的方法,并且因为它是java语言内置的,如果你需要以C ++或Python编写的应用程序,那么共享数据也不会很好 。
- 您可以发明一种将数据项编码为单个字符串的ad-hoc方式,例如将4个ints编码为“12:3:-23:67”。 这是一个简单而灵活的方法,尽管它需要编写一次性编码和解析代码,但解析运行时成本很小。 这最适合编码非常简单的数据。
- 将数据序列化为XML。
而Protocol Buffers是灵活,高效,自动化的来解决这个问题的解决方案。 使用Protocol Buffers您可以编写一个.proto描述您希望存储的数据结构。 从那里,Protocol Buffers编译器创建一个实现自动编码和解析Protocol Buffers数据的类,并使用高效的二进制格式。 生成的类为组成Protocol Buffers的字段提供getter和setter,并且将单元中的协议缓冲区的读取和写入细节。 重要的是,Protocol Buffers格式支持延长格式随时间推移的想法,使得代码仍然可以读取以旧格式编码的数据。
Defining Your Protocol Format
使用Protocol Buffers 首先需要定义一个后缀名为proto的文件(addressbook.proto)。
.proto文件中的定义很简单:您要为要序列化的每个数据结构添加消息,然后为消息中的每个字段指定名称和类型。 这是定义您的消息的.proto文件,addressbook.proto。
addressbook.proto
syntax = "proto2";
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 phones = 4;
}
message AddressBook {
repeated Person people = 1;
}
package tutorial :.proto文件 首先定义了一个包名(tutorial)主要用来避免不同project之间包命名冲突的问题。在java中如果没有定义java_package,就以package(这里tutorial)作为包名。
java_package :生成java类的包名,如果生成其他类文件,则忽略次项。
message:定义消息。上文的addressbook.proto文件,定义了3个消息,Person 、PhoneNumber 、AddressBook。
required :表示消息中,这个参数是必须的,optional 表参数可选。repeated,表该属性可以重复多少次数(包括零),将重复的字段视为动态大小的数组。
完整的.proto文件语法,参考Protocol Buffer Language Guide。
Compiling Your Protocol Buffers
- 下载编译器,前面已经讲到。
- 执行编译需要指定源文件目录,和生成文件目录,这个好理解。由 SRCDIR表示源文件目录, DST_DIR表示输出文件目录(也就是要生成的java类文件存放目录)。如果不指定,将会默认问当前路径。
protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/addressbook.proto
这里的编译示例如下:
- 解压下载的protoc-3.3.0-win32.zip,假设解压后目录 ${PROTO_HOME}。
- 打开命令行工具,
cd /d ${PROTO_HOME}/bin
。 - 执行
protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/addressbook.proto
。假设$SRC_DIR
目录为D:/SRC_DIR
,并且addressbook.proto文件放置在该目录下 。$DST_DIR
设定为D:\DST_DIR
。执行完成,可在$DST_DIR
目录下看到AddressBookProtos.java文件。
Writing A Message
import com.example.tutorial.AddressBookProtos.AddressBook;
import com.example.tutorial.AddressBookProtos.Person;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.PrintStream;
class AddPerson {
// This function fills in a Person message based on user input.
static Person PromptForAddress(BufferedReader stdin,
PrintStream stdout) throws IOException {
Person.Builder person = Person.newBuilder();
stdout.print("Enter person ID: ");
person.setId(Integer.valueOf(stdin.readLine()));
stdout.print("Enter name: ");
person.setName(stdin.readLine());
stdout.print("Enter email address (blank for none): ");
String email = stdin.readLine();
if (email.length() > 0) {
person.setEmail(email);
}
while (true) {
stdout.print("Enter a phone number (or leave blank to finish): ");
String number = stdin.readLine();
if (number.length() == 0) {
break;
}
Person.PhoneNumber.Builder phoneNumber =
Person.PhoneNumber.newBuilder().setNumber(number);
stdout.print("Is this a mobile, home, or work phone? ");
String type = stdin.readLine();
if (type.equals("mobile")) {
phoneNumber.setType(Person.PhoneType.MOBILE);
} else if (type.equals("home")) {
phoneNumber.setType(Person.PhoneType.HOME);
} else if (type.equals("work")) {
phoneNumber.setType(Person.PhoneType.WORK);
} else {
stdout.println("Unknown phone type. Using default.");
}
person.addPhones(phoneNumber);
}
return person.build();
}
// Main function: Reads the entire address book from a file,
// adds one person based on user input, then writes it back out to the same
// file.
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.err.println("Usage: AddPerson ADDRESS_BOOK_FILE");
System.exit(-1);
}
AddressBook.Builder addressBook = AddressBook.newBuilder();
// Read the existing address book.
try {
addressBook.mergeFrom(new FileInputStream(args[0]));
} catch (FileNotFoundException e) {
System.out.println(args[0] + ": File not found. Creating a new file.");
}
// Add an address.
addressBook.addPeople(
PromptForAddress(new BufferedReader(new InputStreamReader(System.in)),
System.out));
// Write the new address book back to disk.
FileOutputStream output = new FileOutputStream(args[0]);
addressBook.build().writeTo(output);
output.close();
}
}
Reading A Message
import com.example.tutorial.AddressBookProtos.AddressBook;
import com.example.tutorial.AddressBookProtos.Person;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintStream;
class ListPeople {
// Iterates though all people in the AddressBook and prints info about them.
static void Print(AddressBook addressBook) {
for (Person person: addressBook.getPeopleList()) {
System.out.println("Person ID: " + person.getId());
System.out.println(" Name: " + person.getName());
if (person.hasEmail()) {
System.out.println(" E-mail address: " + person.getEmail());
}
for (Person.PhoneNumber phoneNumber : person.getPhonesList()) {
switch (phoneNumber.getType()) {
case MOBILE:
System.out.print(" Mobile phone #: ");
break;
case HOME:
System.out.print(" Home phone #: ");
break;
case WORK:
System.out.print(" Work phone #: ");
break;
}
System.out.println(phoneNumber.getNumber());
}
}
}
// Main function: Reads the entire address book from a file and prints all
// the information inside.
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.err.println("Usage: ListPeople ADDRESS_BOOK_FILE");
System.exit(-1);
}
// Read the existing address book.
AddressBook addressBook =
AddressBook.parseFrom(new FileInputStream(args[0]));
Print(addressBook);
}
}
高级用法
Protocol buffers的用法远不止简单的加速和序列化。在 Java API reference可以了解更多信息。
参考
- protobuf安装及C++版实现示例。
http://www.cnblogs.com/luoxn28/p/5303517.html - Protobuf简单使用。
http://www.iloveandroid.net/2015/10/08/studyPtorobuf/