文章大纲
引言
前一篇文章Android进阶——性能优化之一种更高效更轻量的序列化方案Protocol Buffer完全攻略(十)总结了Protobuf的一些基础知识以及内部底层的编解码特点和原理,这一篇就好好的总结下Protobuf的应用。以下是性能优化系列的链接地址列表(持续更新):
- Android进阶——性能优化之APP启动时黑白屏的根源解析及对应的优化措施小结(一)
- Android进阶——性能优化之APP启动过程相关源码解析(二)
- Android进阶——性能优化之APP启动速度优化实战总结(三)
- Android进阶——性能优化之布局渲染原理和底层机制详解(四)
- Android进阶——性能优化之布局优化实战经验小结(五)
- Android进阶——性能优化之内存管理机制和垃圾采集回收机制(六)
- Android进阶——性能优化之内存泄漏和内存抖动的检测及优化措施总结(七)
- Android进阶——性能优化之进程提权与保活原理及手段完全解析(八)
- Android进阶——性能优化之进程提权与拉活原理及手段完全解析(九
- Android进阶——性能优化之一种更高效更轻量的序列化方案Protocol Buffer完全攻略(十)
- Android进阶——性能优化之一种更高效更轻量的序列化方案Protocol Buffer完全攻略(十一)
一、Protocol Buffer的使用步骤
使用Protocol Buffer很简单,无论是任何平台下都是三大步骤:
- 创建.proto 源文件
- 编译.proto 源文件
类似于java文件最终被编译成class 字节码文件,需要依赖于JDK开发环境,Protobuf 也类似需要依赖它自己的编译环境,可以提供的类似SDK包中的protoc.exe把proto文件编译为对应平台下运行的文件,也可以通过IDE插件完成编译,无论何种方式本质上都是一样的,下载protoc-3.8.0-win64 并解压之后就可以通过 protoc -h命令查看帮助,需要对应的指令完成编译,比如说使用protoc --java_out=. xxx.proto编译Java环境下使用的类。
- 使用编译后的产物中的提供方法
二、在Android Studio使用Protobuf
Android Studio使用Protobuf比较便捷,因为Android Studio 为我们提供了对应的插件,使得我们只需要去创建.proto源文件就可以了,无需去手动执行编译任务,以下是通用的几大步骤:
1、在Android Studio项目中引入protobuf-gradle-plugin Gradle插件
在项目根目录下的build.gradle脚本下引入protobuf-gradle-plugin插件
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.0'
//引入protobuf-gradle-plugin Gradle 插件
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.8'
}
}
2、在需要使用Protobuf 的Module下的build.gradle使用插件并进行对应的配置
- 应用protobuf-gradle-plugin 插件
- 在与android 节点同级下配置 protobuf节点(这个protobuf节点就是由protobuf-gradle-plugin 插件提供的)
- 引入Google 官方提供的库依赖
apply plugin: 'com.android.application'
//应用protobuf插件
apply plugin: 'com.google.protobuf'
//配置protobuf插件信息
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.0.0'
}
plugins {
javalite {
artifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0'
}
}
generateProtoTasks {
all().each { task ->
task.builtins {
remove java
}
task.plugins {
javalite { }
}
}
}
}
android{
...
}
dependencies {
...
implementation 'com.google.protobuf:protobuf-lite:3.0.0'
}
3、创建proto文件
经过以上第2、3步骤之后,就可以在项目main主集下新建 proto 目录(即在src/main/目录下创建),再在 proto 目录创建后缀名为.proto的源文件,文件名可以任意取,与.proto文件内部所描述的数据结构无直接联系。
//使用protobuf的版本
syntax = "proto2";
//可省
package tutorial2;
//编译之后生成类所在的包名
option java_package = "com.example.tutorial";
//编译之后生成类所在的类名
option java_outer_classname = "AddressBookProtos";
//message 在Java 环境下可以看成是关键字 class
message Person {
//required 表示必须设置值(不能为null),后面的1 并不是赋初始值的作用,是Field_Number,编码时会参与到Tag的计算中
required string name = 1;
//前面required等 可以看成是注解或者修饰符的作用,紧跟着就是数据类型,然后是变量名
required int32 id = 2;
//optional 表示可选项
optional string email = 3;
//枚举类PhoneType在Java环境下会编译为Person的内部枚举类
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
//类PhoneNumber在Java环境下会编译为Person的内部类
message PhoneNumber {
required string number = 1;
//设置默认值
optional PhoneType type = 2 [default = HOME];
}
//repeated 存储重复的数据 (比如集合)
repeated PhoneNumber phones = 4;
}
message AddressBook {
repeated Person people = 1;
}
配置环境成功之后并编写完毕,编译项目之后,就会在xx/build/generated/source/proto/debug/javalite目录下看到源.proto文件编译后对应的Java类:
内部结构对比如下图:
4、使用protobuf 进行序列化和反序列化
- 通过类似构造者模式newBuilder方法来创建对象
- 通过对象调用自带的toByteArray方法实现序列化操作
- 通过对象调用自带的parseFrom方法实现反序列化操作
package com.crazymo.protobuf;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import com.crazymo.protobuf.bean.JsonTest;
import com.example.tutorial.AddressBookProtos;
import com.google.protobuf.InvalidProtocolBufferException;
public class MainActivity extends AppCompatActivity {
final static String TAG="CrazyMo_";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
testProtobuf();
}
void testProtobuf() {
//通过类似构造者模式来创建对象
AddressBookProtos.Person.PhoneNumber.Builder builder = AddressBookProtos.Person
.PhoneNumber.newBuilder().setNumber("666");
AddressBookProtos.Person.Builder person = AddressBookProtos.Person.newBuilder().setName
("CrazyMo")
.setId(1).addPhones(builder);
AddressBookProtos.Person.PhoneNumber.Builder builder2 = AddressBookProtos.Person
.PhoneNumber.newBuilder().setNumber("168");
AddressBookProtos.Person.Builder person2 = AddressBookProtos.Person.newBuilder().setName
("cmo")
.setId(2).addPhones(builder2);
AddressBookProtos.AddressBook addressBook = AddressBookProtos.AddressBook.newBuilder()
.addPeople(person).addPeople(person2).build();
// 序列化操作,将对象转成 byte数组,比如保存为一个文件
long currTime = System.currentTimeMillis();
byte[] bytes = addressBook.toByteArray();
Log.e(TAG, "protobuf 序列化耗时:" + (System.currentTimeMillis() - currTime));
Log.e(TAG, "protobuf 序列化数据大小:" + bytes.length);
// 反序列化操作,比如从文件 读到内存 或者 解析从服务器返回的数据
try {
currTime = System.currentTimeMillis();
AddressBookProtos.AddressBook addressBook1 = AddressBookProtos.AddressBook.parseFrom
(bytes);
Log.e(TAG, "protobuf 反序列化耗时:" + (System.currentTimeMillis() - currTime));
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
//简单对比下Json的解析
JsonTest.fastJson();
JsonTest.gson();
}
}
源码传送门
https://blog.csdn.net/mzpmzk/article/details/80824839
https://www.jianshu.com/p/92dbe1ef0054
https://www.jianshu.com/p/2a5aa5ac6cf6
https://www.ibm.com/developerworks/cn/linux/l-cn-gpb/index.html
https://www.jianshu.com/p/3504d4643dba
https://github.com/protocolbuffers/protobuf
https://github.com/protocolbuffers/protobuf/releases/tag/v3.8.0
https://developers.google.com/protocol-buffers/docs/downloads
https://developers.google.com/protocol-buffers/docs/javatutorial
来源:https://blog.csdn.net/CrazyMo_/article/details/94592297