Java/Android 中使用Protocol Buffers传输数据

  • JavaAndroid 中使用Protocol Buffers传输数据
    • 下载Protocol Buffers
    • 编写Proto文件
      • 参数声明
      • 参数类型
      • 补充说明
      • 最终的proto文件
    • 生成Java文件
    • 解析数据
    • 演示GIF

Java/Android 中使用Protocol Buffers传输数据

Protocol Buffers是一种数据交互格式,是Google公司开源的一种数据描述语言,能够将结构化数据序列化,可用于数据存储、通信协议等方面。相比于现在流行的XML以及Json格式存储数据,通过Protocol Buffers来定义的文件体积更小,解析速度更快(官方文档中明确提到),目前已经支持很多的主流语言,本篇文章主要介绍一下如何在Java/Android中使用。

Github地址

官方地址

下载Protocol Buffers

在官方地址或者maven仓库中下载即可。

编写Proto文件

本文采用官方文档中的例子来演示一下:官方文档例子地址
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

补充说明:

  • 可以在一个.proto文件中定义多个message类型。这在定义多个相关消息时非常有用:例如,如果要定义与SearchResponse消息类型相对应的回复消息格式,则可以将其添加到相同的.proto:
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.
}

最终的proto文件

这里依然选择采用官方文档中的例子,我们对其进行一些小改动,将包名和生成的类名改成我们自己的:

// 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]

生成Java文件

这里就需要通过刚才下载过来的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();
    }
}

演示GIF

你可能感兴趣的:(Android)