Protocol Buffers是一种数据交互格式,是Google公司开源的一种数据描述语言,能够将结构化数据序列化,可用于数据存储、通信协议等方面。相比于现在流行的XML以及Json格式存储数据,通过Protocol Buffers来定义的文件体积更小,解析速度更快(官方文档中明确提到),目前已经支持很多的主流语言,本篇文章主要介绍一下如何在Java/Android中使用。
Github地址
官方地址
在官方地址或者maven仓库中下载即可。
本文采用官方文档中的例子来演示一下:官方文档例子地址
proto文件中,Message类似Java中的类,里面可以定义我们需要的属性:
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;
}
通过上面的官方例子,我们可以知道Protocol Buffers是支持枚举的。
You specify that message fields are one of the following:
required: a well-formed message must have exactly one of this field.
optional: a well-formed message can have zero or one of this field (but not more than one).
repeated: this field can be repeated any number of times (including zero) in a well-formed message. The order of the repeated values will be preserved.
required:一个message中必须有一个的字段。
optional:一个message中可选的:可以有1一个也可以有0个。
repeated:在一个message中可以重复任意次(包括0),但是会保留他们的顺序。
可以理解为Java中的集合
下面是官方文档中定义proto格式的文件中参数的类型,与其他语言中的数据类型的对应关系:
proto Type | C++ Type | Java Type | Python Type[2] | Go Type |
---|---|---|---|---|
double | double | double | float | *float64 |
float | float | float | float | *float32 |
int32 | int32 | int | int | *int32 |
int64 | int64 | long | int/long[3] | *int64 |
uint32 | uint32 | int[1] | int/long[3] | *uint32 |
uint64 | uint64 | long[1] | int/long[3] | *uint64 |
sint32 | int32 | int | int | *int32 |
sint64 | int64 | long | int/long[3] | *int64 |
fixed32 | uint32 | int[1] | int | *uint32 |
fixed64 | uint64 | long[1] | int/long[3] | *uint64 |
sfixed32 | int32 | int | int | *int32 |
sfixed64 | int64 | long | int/long[3] | *int64 |
bool | bool | boolean | bool | *bool |
string | string | String | str/unicode[4] | *string |
bytes | string | ByteString | str | []byte |
message SearchRequest {
required string query = 1;
optional int32 page_number = 2;
optional int32 result_per_page = 3;
}
message SearchResponse {
...
}
To add comments to your .proto files, use C/C++-style // syntax.
message SearchRequest {
required string query = 1;
optional int32 page_number = 2;// Which page number do we want?
optional int32 result_per_page = 3;// Number of results to return per page.
}
这里依然选择采用官方文档中的例子,我们对其进行一些小改动,将包名和生成的类名改成我们自己的:
// See README.txt for information and build instructions.
//
// Note: START and END tags are used in comments to define sections used in
// tutorials. They are not part of the syntax for Protocol Buffers.
//
// To get an in-depth walkthrough of this file and the related examples, see:
// https://developers.google.com/protocol-buffers/docs/tutorials
// [START declaration]
syntax = "proto3";
package lhy;
// [END declaration]
// [START java_declaration]
option java_package = "com.csdn.lhy"; //指定生成Java文件的包名
option java_outer_classname = "PersonInfo"; //指定生成的Java文件的类名
// [END java_declaration]
// [START csharp_declaration]
option csharp_namespace = "Google.Protobuf.Examples.AddressBook";
// [END csharp_declaration]
// [START messages]
message Person {
string name = 1;
int32 id = 2; // Unique ID number for this person.
string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phones = 4;
}
// Our address book file is just one of these.
message AddressBook {
repeated Person people = 1;
}
// [END messages]
这里就需要通过刚才下载过来的exe工具生成相应的文件:
命令行: proto.exe --java_out=./ ./addressbook.proto
我们可以看到在指定包的文件下生成了我们刚才指定的Java类:
这里要说明的是,addressbook.proto
这个就是我们刚才定义的proto文件。
我们需要将我们刚才下的jar包以及生成的Java文件拷贝到我们的项目中,实际在项目中,我们一般通过通过流来接受、发送数据,生成的Java文件中已经帮我们处理:
toByteArray()
:
可以将该对象序列化为一个Byte数组,进而封装成流。
parseFrom()
:
可以将一个byte数据或者流中解析出该Java类的对象。
示例代码:
package com.csdn.lhy.protobuffersdemo;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import static com.csdn.lhy.protobuffersdemo.PersonInfo.Person.PhoneType.HOME;
public class MainActivity extends AppCompatActivity{
private PersonInfo.Person lhy;
private Button serialize;
private Button deserialize;
private TextView tv_info;
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView(){
serialize = (Button) findViewById(R.id.serialize);
deserialize = (Button) findViewById(R.id.deserialize);
tv_info = (TextView) findViewById(R.id.tv_info);
serialize.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
serializePerson();
}
});
deserialize.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
deserializePerson();
}
});
}
private void deserializePerson(){
try{
PersonInfo.Person dese_lhy = PersonInfo.Person.parseFrom(new ByteArrayInputStream(lhy.toByteArray()));
StringBuffer sb = new StringBuffer();
String newLine = "\n";
sb.append("Name: "+dese_lhy.getName());
sb.append(newLine);
sb.append("Id: "+ dese_lhy.getId());
sb.append(newLine);
sb.append("Phone Num: "+ dese_lhy.getPhones(0));
sb.append(newLine);
tv_info.setText(sb.toString());
}catch(IOException e){
e.printStackTrace();
}
}
private void serializePerson(){
//创建Person对象
lhy = PersonInfo.Person.newBuilder()
.setName("Lhy")
.setId(110)
.setEmail("Vive la Janee D'Arc")
.addPhones(PersonInfo.Person.PhoneNumber.newBuilder()
.setNumber("120")
.setType(HOME).build())
.build();
}
}