Android Studio使用ProtocolBuffers

protobuf的java庫比較大,為滿足android移動設(shè)備在內(nèi)存、性能等各方面的要求,google也推出了android定制版protobuf-lite庫。

使用步驟

  1. 在 .proto 文件中定義消息格式。
    語法學(xué)習(xí)可參考官方文檔。
    語法有syntax2 syntax3的區(qū)分,可在書寫schema的時候聲明用哪個版本,3相對2來說有更好的壓縮特性。
  2. 使用 Protocol Buffer 編譯器編譯生成所需的java文件。
    編譯器生成及用法參見http://www.itdecent.cn/p/e8712962f0e9
    每次手動執(zhí)行 Protocol Buffers 編譯器將 .proto 文件轉(zhuǎn)換為Java文件顯然有點太麻煩,因此google提供了一個Android Studio gradle插件 protobuf-gradle-plugin ,以便于在我們項目的編譯期間自動地執(zhí)行 Protocol Buffers 編譯器。
  3. 使用Java Protocol Buffer API讀寫消息。
    整個使用過程如下圖



    下面就以Demo展示protobuf-gradle-plugin+protobuf-lite庫實現(xiàn)消息序列化和反序列化的過程。

gradle集成protobuf-gradle-plugin

app module的gradle腳本如下:

apply plugin: 'com.android.application'
apply plugin: 'com.google.protobuf' //在gradle腳本開始處聲明依賴的插件

buildscript {
    repositories {
        mavenCentral()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.3'
        classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.0'//配置plugin的版本信息
    }
}
android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId "com.dy.testprotocolbuffer"
        minSdkVersion 15
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
//編寫編譯任務(wù),調(diào)用plugin編譯生成java文件
protobuf {
    protoc {
        artifact = 'com.google.protobuf:protoc:3.0.0'//編譯器版本
    }
    plugins {
        javalite {
            artifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0'//指定當(dāng)前工程使用的protobuf版本為javalite版,以生成javalite版的java類
        }
    }
    generateProtoTasks.generatedFilesBaseDir = "$projectDir/src/main/java" //指定編譯生成java類的存放位置
    generateProtoTasks {
        all().each { task ->
            task.plugins {
                javalite {
                    outputSubDir = '' //指定存放位置的二級目錄,這里未指定
                }
            }
        }
    }
}
//指定原始.proto文件的位置
android {
    sourceSets {
        main {
            java {
                srcDirs 'src/main/java'
            }
            proto {
                srcDirs 'src/main/proto'
            }
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.1.0'
    compile 'com.google.protobuf:protobuf-lite:3.0.0' //依賴protobuf-lite庫
}

工程目錄如下:

pb工程目錄.png

關(guān)于protobuf-gradle-plugin的更多用法可參考官方文檔https://github.com/google/protobuf-gradle-plugin

schema編寫(.proto文件)

我們將以下Json示例轉(zhuǎn)為pb格式:

{
    "data":[
      {
        "datatype":1,
        "itemdata":
            {//共有字段45個
              "sname":"\u5fae\u533b",
              "packageid":"330611",
              …
              "tabs":[
                        {
                          "type":1,
                          "f":"abc"
                        },
                        …
              ]
            }
      },
      …
    ],
    "hasNextPage":true,
    "dirtag":"soft"
  }

schema編寫如下:

syntax = "proto2";
package com.dy.messagepackdemo.protobuffer.model;

option java_package = "com.dy.messagepackdemo.protobuffer.model";
option java_outer_classname = "ResponsePB";

message Tab {
  required int32 type = 1;
  optional string f = 2;
}
message ItemData {
    required string sname = 1;
    required string packageid = 2;
    ...
    repeated Tab tabs = 45;
}
message DataItem {
    required int32 datatype = 1;
    required ItemData itemdata = 2;
}
message ResponsePB {
    repeated DataItem data = 1;
    required bool hasNextPage = 2;
    required string dirtag = 3;
}

這個schema已經(jīng)將上面json示例的層次結(jié)構(gòu)體現(xiàn)的很明顯了。簡單解釋一下各參數(shù)的意義及用法:

  • syntax指定用哪個版本的語法,proto3比2有更好的壓縮特性
  • package指定編譯生成java類的包名
  • java_outer_classname指定java類的類名
  • 修飾符:required表示必填,optional可選,repeated表示重復(fù)的list
  • 每個層級內(nèi)的數(shù)據(jù)要按順序編號,也就是上一篇文中講的field_number
  • 每個結(jié)構(gòu)體是一個message,message之間可以互相引用。

編譯

build工程,可以看到在java包下生成了debug目錄(由于當(dāng)前是debug模式),再下面就是我們想要的包及schema編譯后的java文件。



使用編譯生成的java類,就可以進行數(shù)據(jù)的序列化和反序列化了。

序列化操作

構(gòu)造一個response數(shù)據(jù)并寫入文件

public static void writeResponseToPbFile(String pbfilepath, ResponseJson responseJson) {
        File fproto = new File(pbfilepath);
        if (!fproto.exists()) {
            try {
                fproto.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //build response
        //構(gòu)造builder
        ResponsePB.Response.Builder responseBuilder = ResponsePB.Response.newBuilder();
        //填充數(shù)據(jù)
        responseBuilder.setHasNextPage(resultJson.hasNextPage);
        responseBuilder.setDirtag(resultJson.dirtag);
        ...//此處省略若干行
        //結(jié)束 build
        ResponsePB.Response response = responseBuilder.build();
        //寫文件
        try {
            FileOutputStream foProto = new FileOutputStream(fproto);
            response.writeTo(foProto);
            foProto.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

用法很簡單,生成對象的構(gòu)造器builder,用提供的各種Set方法填充數(shù)據(jù),最后build,二進制數(shù)據(jù)就生成了。用法跟普通的pojo類沒有區(qū)別。

反序列化

將文件中的數(shù)據(jù)解析到Response對象中

public static void parseXinruiPb(byte[] bytes) {
    ResponsePB.Response response = ResponsePB.Response.parseFrom(bytes);
    boolean hasNextPage = response.getHasNextPage();
    String dirtag = response.getDirtag();
    ...
}

用起來也非常簡單,parseFrom搞定。 值得一提的是,由于protobuf的存儲結(jié)構(gòu)決定了它在進行數(shù)據(jù)解析的時候必須將整個數(shù)據(jù)完整解析一遍才能得到你想要的數(shù)據(jù),也就是數(shù)據(jù)傳輸過程中所謂的封包-解析過程,這與json解析的過程類似,區(qū)別在于它對key鍵的特殊編碼,省去了字符匹配的過程。

更高級的用法--動態(tài)編譯

Protobuf 提供了 google::protobuf::compiler 包來完成動態(tài)編譯的功能。感興趣的同學(xué)可以自行研究。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評論 19 139
  • 不想再假裝堅強 也不堅強 其實我并不是外表看上去的不在乎無所謂 哪個女生不期望愛情 不想要愛人的陪伴 所說的慶幸自...
    小沐oc閱讀 167評論 0 1
  • 1. 編輯相關(guān)快捷鍵 2. 查看和定位快捷鍵 3. 調(diào)試快捷鍵 Eclipse中有如下一些和運行調(diào)試相關(guān)的快捷鍵。...
    dongbingliu閱讀 442評論 4 3
  • 我在社區(qū)醫(yī)院上班,要接觸不少慢性病患者,給他們配藥是我工作的一部分。這種患者買藥的特點是定期配相同的藥,除非有特殊...
    迎風(fēng)小葉子閱讀 560評論 1 1
  • 想念是無法控制的情緒。 無論在安靜或喧囂的環(huán)境里,不能控制自己,我好想你。 我想每天能看著你,每天能抱抱你 ...
    知秋業(yè)閱讀 264評論 1 0

友情鏈接更多精彩內(nèi)容