(十八)Protobuf —— 优化序列化,替换 Json

版权声明:本文为博主原创文章,未经博主允许不得转载。
本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。

一、Protobuf 介绍

google 开源项目。序列化数据结构的方案(Json 、XML 也是),通常用于编写需要数据交换或者需要存储数据的程序。这套方案包含一种用于描述数据结构的接口描述语言(Interface Description Language)和一个生成器,用于生成描述该数据结构的不同编程语言的源代码。

序列化与反序列化:

序列化 : 将数据结构或对象转换成二进制串的过程

反序列化:将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程

优缺点:
在这里插入图片描述
总结:
Protobuf 比 XML、Json 更小,更快,使用和维护更简单,但是使用范围不够广。

二、Protobuf 使用

我们可以参照 Protobuf 官网 的指南进行尝试。选择 Basics: Java 查看 java 语言的用法。

1. 在 main 文件夹下新建 proto 文件夹。
在这里插入图片描述

2. 在 proto 文件夹下新建 test.proto 文件。
在这里插入图片描述

3. 从官网拷贝 demo 代码到 test.proto 文件。

//使用 proto2,目前有 proto2 和 proto3 这两个版本,他们语法有些不同,内核实现也有所区别。
syntax = "proto2";

package tutorial;

//生成 java 源文件的包名
option java_package = "com.example.tutorial";
//生产 java 源文件的类名
option java_outer_classname = "AddressBookProtos";

//message 相当与 java 的 class
message Person {
  //required 必须设置(不能为null)
  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 重复的(也就是指集合)
  repeated PhoneNumber phones = 4;
}

message AddressBook {
  repeated Person people = 1;
}

这是一段 protobuf 的源文件,我们需要使用 protobuf 的编译器进行编译。

4. 编译 protobuf 代码,这边有两种方案进行编译。

(1)手动编译
手动编译的时候,我们需要先拿到编译器。github 下载地址

在这里插入图片描述
这边下载时 win64 版本,解压出来后,在 bin 目录下有 protoc.exe ,我们使用这个进行编译。
在这里插入图片描述
命令:

protoc -I=生成 java 源文件的目录 --java_out=.  proto源文件

在这里插入图片描述
(这边把 proto.exe 和 test.proto 拷贝到 D:\proto 下面,这个指令是生成 java 的源文件,修改指令,也可以生成其他源文件)

运行结果:
在这里插入图片描述
在 com 包一直往下点,可以发现生成一个 AddressBookProtos.java 的 java 源文件。

(2)使用 AS 插件
使用谷歌提供的 gradle 插件 进行编译。

在项目下的 build.gradle 中添加 classpath。
在这里插入图片描述

classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.8'

在这里插入图片描述

在 app 下的 build.gradle 中引用插件,配置参数,以及进行依赖。
在这里插入图片描述
引用插件:

	apply plugin: 'com.google.protobuf'

配置参数:

protobuf {
  protoc {
    // You still need protoc like in the non-Android case
    artifact = 'com.google.protobuf:protoc:3.0.0'
  }
  plugins {
    javalite {
      // The codegen for lite comes as a separate artifact
      artifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0'
    }
  }
  generateProtoTasks {
    all().each { task ->
      task.builtins {
        // In most cases you don't need the full Java output
        // if you use the lite output.
        remove java
      }
      task.plugins {
        javalite { }
      }
    }
  }
}

进行依赖:

    implementation 'com.google.protobuf:protobuf-lite:3.0.0'

这样 AS 就会自动帮我们进行编译前面编写的 proto 文件。

5. 运行 Make Modele app。

在这里插入图片描述
运行完成之后,我们可以在 build 文件夹下,找到 proto 生成的源文件。

在这里插入图片描述
这个源文件最终会被遗弃打包到 apk 中。

6. 编写代码使用。

MainActivity :

public class MainActivity extends AppCompatActivity {

	private final static String TAG = "MainActivity";

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		AddressBookProtos.Person.PhoneNumber.Builder phoneNumber = AddressBookProtos.Person.PhoneNumber
				.newBuilder().setNumber("13511111111");
		AddressBookProtos.Person.Builder person = AddressBookProtos.Person.newBuilder()
				.setName("张三").setId(1).addPhones(phoneNumber);

		AddressBookProtos.Person.PhoneNumber.Builder phoneNumber1 = AddressBookProtos.Person.PhoneNumber
				.newBuilder().setNumber("13522222222");
		AddressBookProtos.Person.Builder person1 = AddressBookProtos.Person.newBuilder()
				.setName("李四").setId(1).addPhones(phoneNumber1);

		AddressBookProtos.AddressBook addressBook = AddressBookProtos.AddressBook.newBuilder()
				.addPeople(person).addPeople(person1)
				.build();

		//保存为一个文件或者需要向服务器发送
		//序列化,将对象转成 byte 数组
		long l = System.currentTimeMillis();
		byte[] bytes = addressBook.toByteArray();
		Log.e(TAG, "111 zx protobuf 序列化耗时:" + (System.currentTimeMillis() - l));
		Log.e(TAG, "111 zx protobuf 序列化数据大小:" + bytes.length);

		//从文件读到内存或者解析从服务器返回的数据
		//反序列化
		try {
			l = System.currentTimeMillis();
			AddressBookProtos.AddressBook addressBook1 = AddressBookProtos.AddressBook.parseFrom
					(bytes);
			Log.e(TAG, "111 zx protobuf 反序列化耗时:" + (System.currentTimeMillis() - l));
		} catch (InvalidProtocolBufferException e) {
			e.printStackTrace();
		}
	}
}

三、Protobuf 的性能

在这里插入图片描述
这是上面代码运行的结果,我们按照同样的数据格式,同样的数据,对 Json 进行比较。

AddressBook :

public class AddressBook {
    List<Person> persons;

    public AddressBook() {
        this.persons = new ArrayList<>();
    }

    public void addPersons(Person person) {
         persons.add(person);
    }

    public List<Person> getPersons( ) {
        return persons;
    }
}

Person :

public class Person {
    private String name;
    private int id;
    private String email;

    private List<PhoneNumber> phones;

    public Person() {
        phones = new ArrayList<>();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
    
    public void addPhones(PhoneNumber number){
        phones.add(number);
    }

    public List<PhoneNumber> getPhones() {
        return phones;
    }
}

PhoneNumber :

public class PhoneNumber {
    enum PhoneType {
        MOBILE,
        HOME,
        WORK;
    }

    private String number;
    private PhoneType type;

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    public PhoneType getType() {
        return type;
    }

    public void setType(PhoneType type) {
        this.type = type;
    }
}

编写 JsonTest ,分别使用 gson 和 fastjson 进行测试。

JsonTest :

public class JsonTest  {

    private static final String TAG = "JsonTest";

    public static void fastJson() {

        AddressBook addressBook = getObject();

        long l = System.currentTimeMillis();
        String data = JSON.toJSONString(addressBook);
        byte[] bytes = data.getBytes();
        Log.e(TAG, "111 zx FastJson 序列化耗时:" + (System.currentTimeMillis() - l));
        Log.e(TAG, "111 zx FastJson 序列化数据大小:" + bytes.length);

        l = System.currentTimeMillis();
        AddressBook addressBook1 = JSON.parseObject(new String(bytes), AddressBook.class);
        Log.e(TAG, "111 zx FastJson 反序列化耗时:" + (System.currentTimeMillis() - l));
    }
    
    public static void gson(){
        AddressBook addressBook = getObject();
        long l = System.currentTimeMillis();
        Gson gson = new Gson();
        String data = gson.toJson(addressBook);
        byte[] bytes = data.getBytes();
        Log.e(TAG, "111 zx Gson 序列化耗时:" + (System.currentTimeMillis() - l));
        Log.e(TAG, "111 zx Gson 序列化数据大小:" + bytes.length);

        l = System.currentTimeMillis();

        AddressBook addressBook1 = gson.fromJson(new String(bytes), AddressBook.class);
        Log.e(TAG, "111 zx Gson 反序列化耗时:" + (System.currentTimeMillis() - l));
    }

    private static AddressBook getObject(){
        AddressBook addressBook = new AddressBook();

        PhoneNumber p_110 = new PhoneNumber();
        p_110.setNumber("110");
        Person zs = new Person();
        zs.setId(1);
        zs.setName("张三");
        zs.addPhones(p_110);
        addressBook.addPersons(zs);


        PhoneNumber p_120 = new PhoneNumber();
        p_120.setNumber("120");
        Person ls = new Person();
        ls.setId(2);
        ls.setName("李四");
        ls.addPhones(p_120);
        addressBook.addPersons(ls);
        return addressBook;
    }
}

结果:
在这里插入图片描述
可以发现,不论序列化耗时、序列化后数据大小、反序列化,protobuf 都有明显的优势。

四、附

代码链接

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值