Protobuf简介与简单使用

  • 内容
  • 评论
  • 相关

Protobuf是一个高性能、易扩展的序列化框架,它的性能测试有关数据可以参看官方文档。通常在TCP Socket通讯(RPC调用)相关的应用中使用。当然后的人说Protobuf 是冲着跨语言无歧义的 IDL 的去的,才不仅仅是因为性能!这个我不管,没有调查研究过,下面只给使用方法。

下面我们就在AndroidStudio中使用一下,新建一个工程,然后工程根目录下的gralde中的dependencies中添加:

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

app的module的gradle添加如下内容(自己找有用的):

apply plugin: 'com.google.protobuf'
apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "com.velsharoon.protobuf"
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    sourceSets {
        main {
            java {
                srcDir 'src/main/java'
            }
            // 定义proto文件目录
            proto {
                srcDir 'src/main/proto'
                include '**/*.proto'
            }
        }
    }
}

dependencies {
    compile "com.google.protobuf:protobuf-java:3.1.0"
}

protobuf {
    //配置protoc编译器
    protoc {
        artifact = 'com.google.protobuf:protoc:3.1.0'
    }
    //这里配置生成目录,编译后会在build的目录下生成对应的java文件
    generateProtoTasks {
        all().each { task ->
            task.builtins {
                remove java
            }
            task.builtins {
                java {}
            }
        }
    }
}

我们创建一个Person.proto文件,然后内容如下:

syntax = "proto3";
package com.velsharoon.protobuf;
option java_outer_classname = "Person";
message Info {
    string name = 1;
    int32 age = 2;
    string sex = 3;

    message Address {
      string province = 1;
      string city = 2;
      string area = 3;
    }

    Address address = 4;

    message Favorite{
      string what = 1;
    }

    repeated Favorite favorite = 5;
}

简单的说一下上面的内容:

  • syntax指定protobuf的版本,不写的话是2
  • package就是生成文件所在的包了
  • java_outer_classname就是生成的文件名了
  • message一般就可以理解为类了
  • 每个字段对应的数字的值就是对应数据的位置
  • repeated就是重复,代表这是一个列表

第二版的字段支持写require和optional,第三版好像不能写了

gradle同步一下就可以生成对应的java文件了,它的路径如下:

生成的这个文件代码量上还是挺大的,看看目录就好了:

大部分内容我们都知道干什么,我们来说几个重要的:

  • 生成的类(这里是Person)是final的,构造函数也是private的。内部有我们的message对应的类(这里是Info)。这个类有一个对应的接口XXXOrBuild(这里是InfoOrBuild),这个接口里有获取各种参数的get/set方法。而上面的类就实现了这个接口(这里Info实现了InfoOrBuild接口)。其他的message也是如此的实现方式。最终数据通过builder方式创建。

下面我们来使用这个类:

    public void test() {
        Person.Info.Address address = Person.Info.Address.newBuilder()
                .setProvince("河东")
                .setCity("高老庄")
                .setArea("水城")
                .build();
        Person.Info.Favorite favorite = Person.Info.Favorite.newBuilder()
                .setWhat("打豆豆")
                .build();
        Person.Info info = Person.Info.newBuilder()
                .setAge(99)
                .setName("velsharoon")
                .setAddress(address)
                .addFavorite(favorite)
                .build();
        Log.e("log", info.getName());
        Log.e("log", "" + info.getAge());
        Log.e("log", "" + info.getAddress().getProvince() + info.getAddress().getCity() + info.getAddress().getArea());
        for (Person.Info.Favorite fvrt : info.getFavoriteList()) {
            Log.e("log", fvrt.getWhat());
        }
//        Person.PersonInfo info = Person.PersonInfo.parseFrom();
//        byte[] data = info.toByteArray();
    }

输出结果是什么,应该不用说了。最后我们注释掉了两行,虽然没有用到,但是它是非常重要的,为什么?因为你不会是只在本地使用protobuf吧,本文最一开始也说了,它的优势是可以传输,所以说我们需要将它变为流发出去,或者接收流把它解析成对象。因此上面的代码给了如何把对象转为字节数组,和如何把目标转为对象(parseFrom可以传各种参数)。

现在我们简单的探讨一下protobuf的基本原理。

protobuf中每个字段的格式为: 修饰符 字段类型 字段名 = 域号; 在序列化时,protobuf按照TLV的格式序列化每一个字段,T即Tag,也叫Key;V是该字段对应的值value;L是Value的长度,如果一个字段是整形,这个L部分会省略。 序列化后的Value是按原样保存到字符串或者文件中,Key按照一定的转换条件保存起来,序列化后的结果就是 KeyValueKeyValue…。Key的序列化格式是按照message中字段后面的域号与字段类型来转换。转换公式如下:

(field_number << 3) | wire_type

好了,烂尾了。。。。。

评论

0条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注