Android进阶——性能优化之一种更高效更轻量的序列化方案Protocol Buffer完全攻略(十一)

本小妞迷上赌 提交于 2019-11-29 16:37:02

引言

前一篇文章Android进阶——性能优化之一种更高效更轻量的序列化方案Protocol Buffer完全攻略(十)总结了Protobuf的一些基础知识以及内部底层的编解码特点和原理,这一篇就好好的总结下Protobuf的应用。以下是性能优化系列的链接地址列表(持续更新):

一、Protocol Buffer的使用步骤

使用Protocol Buffer很简单,无论是任何平台下都是三大步骤:

  • 创建.proto 源文件
  • 编译.proto 源文件

类似于java文件最终被编译成class 字节码文件,需要依赖于JDK开发环境,Protobuf 也类似需要依赖它自己的编译环境,可以提供的类似SDK包中的protoc.exeproto文件编译为对应平台下运行的文件,也可以通过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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!