Android之协议缓冲区(Protocol Buffers)详解

Protocol Buffers谷歌的数据交换格式。

(1)概况

协议缓冲区是Google用于序列化结构化数据的语言无关,跨平台,可扩展的机制。我们可以在github上查看文档。

https://github.com/protocolbuffers/protobuf

还有很多其他的开源工具(thrift,json,xml等等)可方便的处理POJO对象的序列化。

(2)协议编译器安装

协议编译器是用C ++编写的。如果您使用的是C ++,请按照C ++安装说明一起安装protoc和C ++运行时。

对于非C ++用户,安装协议编译器的最简单方法是从我们的发布页面下载预构建的二进制文件:

https://github.com/protocolbuffers/protobuf/releases

在每个版本的下载部分,您可以在zip包中找到预构建的二进制文件:protoc- $ VERSION- $ PLATFORM.zip。它包含protoc二进制文件以及与protobuf一起分发的一组标准.proto文件。

如果您要查找发布页面中未提供的旧版本,请在此处查看maven repo:

https://repo1.maven.org/maven2/com/google/protobuf/protoc/

这些预先构建的二进制文件仅用于已发布的版本。如果你想在HEAD使用github主版本,或者你需要修改protobuf代码,或者你正在使用C ++,建议你从源代码构建自己的protoc二进制文件。

(3)优点
  • 产品非常成熟
  • 跨语言,不局限java
  • 编码后消息很小,利于存储和传输
  • 编码性能高
  • 支持不同版本的协议前后兼容
  • 支持定义可选和必选字段
(4)环境配置

这里环境配置可以查看github中的最新插件配置

https://github.com/google/protobuf-gradle-plugin

这里一定要按照github文档配置,否则如果不是最新的插件很有可能就会配置失败。

【第一步】 配置插件

在项目的根目录的build.gradle文件里添加插件

buildscript {
    repositories {
        //...
        mavenCentral()
    }

    dependencies {
        //...
        classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.8'
    }
}

allprojects {
    repositories {
        //...
        mavenCentral()
    }
}

注意:目前最新的插件版本是0.8.8,在以后,请查看github官网查询最新的插件版本,以防配置失败。

【第二步】 在模块中使用protobuf插件

apply plugin: 'com.android.application'
//protobuf插件
apply plugin: 'com.google.protobuf'

android {
    //....
    sourceSets {
        main {
            proto {
                // In addition to the default 'src/main/proto'
                srcDir 'src/main/proto'
                //srcDir 'src/main/protocolbuffers'
                // In addition to the default '**/*.proto' (use with caution).
                // Using an extension other than 'proto' is NOT recommended,
                // because when proto files are published along with class files, we can
                // only tell the type of a file from its extension.
                include '**/*.proto'
            }
            java {
                //...
            }
        }
        test {
            proto {
                // In addition to the default 'src/test/proto'
                srcDir 'src/test/protocolbuffers'
            }
        }
    }
}

srcDir为proto文件的根目录,include导入此根目录下的所有后缀名为.proto的所有文件。当我们编译程序时,会编译.proto文件。

图片.png

【第三步】 在模块中配置protobuf-lite库

android {
    //....
}
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 { }
            }
        }
    }
}

【第四步】 在指定目录下新建.proto文件,并编辑

图片.png

编辑的内容为

syntax = "proto2";
package com.xxx.ooo.protobuf;
option java_outer_classname = "ProStudent";
//结构体
message ProStudentInfo {
    required int64 mId = 1;
    required string mName = 2;
    required int32 mGender = 3;
}

目前protobuf已经更新到了protobuf3,上面之所以使用protobuf2是因为greendao:3.2.2对protobuf2完美兼容,我想,在greendao以后的版本中,肯定会完美支持protobuf3的。

(5)proto文件编写知识点

protobuf目前主要有两个大版本:proto2 和 proto3。

其中 proto2 支持 Java、Python、 Objective-C、和 C++。
proto3 增加了对Go、JavaNano、Ruby、和 C#的支持。

【知识一】 syntax关键字

声明版本。例如syntax="proto3",如果没有声明,则默认是proto2。

【知识二】 package关键字

声明包名

【知识三】 import关键字

导入包。类似于java,例如:

import "google/protobuf/timestamp.proto";

【知识四】 java_package关键字

指定生成的类应该放在什么Java包名下。如果你没有显式地指定这个值,则它简单地匹配由package声明给出的Java包名。例如:

option java_package = "com.katyusha.aron.demo";

【知识五】 java_outer_classname关键字

定义封装结构体的类名。如果你没有显式地给定java_outer_classname,则将通过把文件名转换为首字母大写来生成。例如:

option java_outer_classname = "ProStudent";

【知识六】 message关键字

类似于java中的class关键字。这里用来定义结构体。

【知识七】 required、optional、repeated关键字

required用于修饰属性,字段只能也必须出现 1 次(proto3中不需要使用)
optional用于修饰属性,字段可出现 0 次或1次(proto3中不需要使用)
repeated用于修饰属性,字段可出现任意多次(包括 0)(其实就是List数组)

【知识八】 基本数据类型

Protobuf定义了一套基本数据类型。几乎都可以映射到C++\Java等语言的基础数据类型。

protobuf 数据类型 描述 打包 C++语言映射
bool 布尔类型 1字节 bool
double 64位浮点数 N double
float 32为浮点数 N float
int32 32位整数 N int
uin32 无符号32位整数 N unsigned int
int64 64位整数 N __int64
uint64 64为无符号整 N unsigned __int64
sint32 32位整数,处理负数效率更高 N int32
sing64 64位整数 处理负数效率更高 N __int64
fixed32 32位无符号整数 4 unsigned int32
fixed64 64位无符号整数 8 unsigned __int64
sfixed32 32位整数、能以更高的效率处理负数 4 unsigned int32
sfixed64 64为整数 8 unsigned __int64
string 只能处理 ASCII字符 N std::string
bytes 用于处理多字节的语言字符、如中文 N std::string
enum 可以包含一个用户自定义的枚举类型uint32 N(uint32) enum
message 可以包含一个用户自定义的消息类型 N object of class

N 表示打包的字节并不是固定。而是根据数据的大小或者长度。

例如int32,如果数值比较小,在0~127时,使用一个字节打包。

关于枚举的打包方式和uint32相同。

关于message,类似于C语言中的结构包含另外一个结构作为数据成员一样。

关于 fixed32 和int32的区别。fixed32的打包效率比int32的效率高,但是使用的空间一般比int32多。因此一个属于时间效率高,一个属于空间效率高。根据项目的实际情况,一般选择fixed32,如果遇到对传输数据量要求比较苛刻的环境,可以选择int32.

【知识九】 枚举

枚举一般展示状态信息

enum StudentType{
    Pupil = 0;//小学生
    MIDDLE_STUDENT = 1;//中学生
    COLLEGE_STUDENT = 2;//大学生
}

【知识十】 内部message

可以通俗的的理解为结构体的内部类。

//结构体
message ProStudentInfo {
     //....
    message ProStu{
        required int64 mId = 1;
        required string mName = 2;
    }

}

[本章完...]

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容