Google Protocol Buffer

      網友投稿 2695 2025-03-31

      Google Protocol Buffer(protobuf)是一種高效且格式可擴展的編碼結構化數據的方法。和JSON不同,protobuf支持混合二進制數據,它還有先進的和可擴展的模式支持。protobuf已在大多數軟件平臺上實現,包括適用于Android的精簡Java版。

      http://developers.google.com/protocol-buffers/上有protobuf文檔,下載鏈接以及安裝說明。需要注意的是,Android平臺為構建精簡版的protobuf,所以不能使用中央Maven倉庫里的版本。在Java源碼目錄內執行mvn package -p lite可以生成精簡版。檢查是否有更多安裝細節。

      JSON允許對JSONObject對象進行任意數據的讀寫操作,但protobuf要求使用模式來定義要存儲的數據。模式會定義一些消息,每個消息包含一些名-值對字段。字段可能是內置的原始數據類型,枚舉或者其他消息。可以指定一個字段是必須的還是可選的,以及其他一些參數。一旦定義好模式,就可以使用protobuf工具生成Java代碼。生成的Java類現在可以很方便地用來讀寫protobuf數據。

      下面的代碼使用protobuf模式定義了Task信息:

      package com.aptl.code.task;

      option optimize_for = LITE_RUNTIME;

      option java_package = "com.aptl.protobuf";

      option java_outer_classname = "TaskProtos";

      message Task {

      enum Status {

      CREATED = 0;

      ONGOING = 1;

      CANCELLED = 2;

      COMPLETED = 3;

      message Owner {

      required string name = 1;

      optional string email = 2;

      optional string phone = 3;

      }

      message Comment {

      required string author = 1;

      required uint32 timestamp = 2;

      required string content = 3;

      }

      required string name = 1;

      required uint64 created = 2;

      required int32 priority = 3;

      required Status status = 4;

      optional Owner owner = 5;

      repeated Comment comments = 6;

      }

      這里將給出以上消息定義的關鍵性說明。

      1. message是消息定義的關鍵字,等同于C++中的struct/class,或是Java中的class。

      2. Task 為消息的名字,等同于結構體名或類名。

      3. required前綴表示該字段為必要字段,既在序列化和反序列化之前該字段必須已經被賦值。與此同時,在Protocol Buffer中還存在另外兩個類似的關鍵字,optional和repeated,帶有這兩種限定符的消息字段則沒有required字段這樣的限制。相比于optional,repeated主要用于表示數組字段。具體的使用方式在后面的用例中均會一一列出。

      4. int64和string分別表示長整型和字符串型的消息字段,在Protocol Buffer中存在一張類型對照表,既Protocol Buffer中的數據類型與其他編程語言(C++/Java)中所用類型的對照。該對照表中還將給出在不同的數據場景下,哪種類型更為高效。該對照表將在后面給出。

      5. name,created ,priority ,status,owner 和comments分別表示消息字段名,等同于Java中的域變量名,或是C++中的成員變量名。

      6. 標簽數字1和2則表示不同的字段在序列化后的二進制數據中的布局位置。在該例中,created字段編碼后的數據一定位于name之后。需要注意的是該值在同一message中不能重復。另外,對于Protocol Buffer而言,標簽值為1到15的字段在編碼時可以得到優化,既標簽值和類型信息僅占有一個byte,標簽范圍是16到2047的將占有兩個bytes,而Protocol Buffer可以支持的字段數量則為2的29次方減一。有鑒于此,我們在設計消息結構時,可以盡可能考慮讓repeated類型的字段標簽位于1到15之間,這樣便可以有效的節省編碼后的字節數量。

      Protocol Buffer允許我們在.proto文件中定義一些常用的選項,這樣可以指示Protocol Buffer編譯器幫助我們生成更為匹配的目標語言代碼。Protocol Buffer內置的選項被分為以下三個級別:

      1. 文件級別,這樣的選項將影響當前文件中定義的所有消息和枚舉。

      2. 消息級別,這樣的選項僅影響某個消息及其包含的所有字段。

      Google Protocol Buffer

      3. 字段級別,這樣的選項僅僅響應與其相關的字段。

      下面將給出一些常用的Protocol Buffer選項。

      1. option java_package = "com.aptl.protobuf";

      java_package是文件級別的選項,通過指定該選項可以讓生成Java代碼的包名為該選項值,如上例中的Java代碼包名為com.aptl.protobuf。與此同時,生成的Java文件也將會自動存放到指定輸出目錄下的com/aptl/protobuf子目錄中。如果沒有指定該選項,Java的包名則為package關鍵字指定的名稱。該選項對于生成C++代碼毫無影響。

      2. option java_outer_classname = "TaskProtos";

      java_outer_classname是文件級別的選項,主要功能是顯示的指定生成Java代碼的外部類名稱。如果沒有指定該選項,Java代碼的外部類名稱為當前文件的文件名部分,同時還要將文件名轉換為駝峰格式,如:my_project.proto,那么該文件的默認外部類名稱將為MyProject。該選項對于生成C++代碼毫無影響。

      注:主要是因為Java中要求同一個.java文件中只能包含一個Java外部類或外部接口,而C++則不存在此限制。因此在.proto文件中定義的消息均為指定外部類的內部類,這樣才能將這些消息生成到同一個Java文件中。在實際的使用中,為了避免總是輸入該外部類限定符,可以將該外部類靜態引入到當前Java文件中,如:import static com.aptl.protobuf.TaskProtos.*。

      3. option optimize_for = LITE_RUNTIME;

      optimize_for是文件級別的選項,Protocol Buffer定義三種優化級別SPEED/CODE_SIZE/LITE_RUNTIME。缺省情況下是SPEED。

      SPEED: 表示生成的代碼運行效率高,但是由此生成的代碼編譯后會占用更多的空間。

      CODE_SIZE: 和SPEED恰恰相反,代碼運行效率較低,但是由此生成的代碼編譯后會占用更少的空間,通常用于資源有限的平臺,如Mobile。

      LITE_RUNTIME: 生成的代碼執行效率高,同時生成代碼編譯后的所占用的空間也是非常少。這是以犧牲Protocol Buffer提供的反射功能為代價的。因此我們在C++中鏈接Protocol Buffer庫時僅需鏈接libprotobuf-lite,而非libprotobuf。在Java中僅需包含protobuf-java-2.4.1-lite.jar,而非protobuf-java-2.4.1.jar。

      注:對于LITE_MESSAGE選項而言,其生成的代碼均將繼承自MessageLite,而非Message。

      4. [pack?= true]: 因為歷史原因,對于數值型的repeated字段,如int32、int64等,在編碼時并沒有得到很好的優化,然而在新近版本的Protocol Buffer中,可通過添加[pack=true]的字段選項,以通知Protocol Buffer在為該類型的消息對象編碼時更加高效。如:

      repeated int32 samples = 4 [packed=true]。

      注:該選項僅適用于2.3.0以上的Protocol Buffer。

      5. [default?= default_value]: optional類型的字段,如果在序列化時沒有被設置,或者是老版本的消息中根本不存在該字段,那么在反序列化該類型的消息是,optional的字段將被賦予類型相關的缺省值,如bool被設置為false,int32被設置為0。Protocol Buffer也支持自定義的缺省值,如:

      optional int32 result_per_page = 3 [default = 10]。

      從InputStream反序列化protobuf對象非常容易,如下例所示。生成的Java代碼提供一些用于合并字節數組,byteBuffer和InputStream對象的函數。

      public static TaskProtos.Task readBrotoBufFromStream(InputStream inputStream)

      throws IOException {

      TaskProtos.Task task = TaskProtos.Task.newBuilder()

      .mergeFrom(inputStream).build();

      Log.d("ProtobufDemo", "Read Task from stream: "

      + task.getName() + ", "

      + new Date(task.getCreated()) + ", "

      + (task.hasOwner() ?

      task.getOwner().getName() : "no owner") + ", "

      + task.getStatus().name() + ", "

      + task.getPriority()

      + task.getCommentsCount() + " comments.");

      return task;

      }

      本例顯示了如何檢索protobuf對象的值。注意:protobuf對象是不可變的。修改它們唯一的方法是從現有對象創建一個新的構建器,設置新的值,并生成一個取代原有對象的Task。這使得protobuf有點不好用,但它強制開發者在持久化復雜對象時使用更好的設計。

      下面的方法顯示了如何構建一個新的protobuf對象。首先為構造的對象創建一個新的Builder,然后設置所需要的值并調用Builder.build()方法來創建不可變的protobuf對象。

      public static TaskProtos.Task buildTask(String name, Date created,

      String ownerName, String ownerEmail,

      String ownerPhone,

      TaskProtos.Task.Status status,

      int priority,

      List comments) {

      TaskProtos.Task.Builder builder = TaskProtos.Task.newBuilder();

      builder.setName(name);

      builder.setCreated(created.getTime());

      builder.setPriority(priority);

      builder.setStatus(status);

      if(ownerName != null) {

      TaskProtos.Task.Owner.Builder ownerBuilder

      = TaskProtos.Task.Owner.newBuilder();

      ownerBuilder.setName(ownerName);

      if(ownerEmail != null) {

      ownerBuilder.setEmail(ownerEmail);

      }

      if(ownerPhone != null) {

      ownerBuilder.setPhone(ownerPhone);

      }

      builder.setOwner(ownerBuilder);

      }

      if (comments != null) {

      builder.addAllComments(comments);

      }

      return builder.build();

      }

      API提供了一系列方法用來把protobuf對象寫到文件或者網絡流中。下面的代碼演示了如何把Task對象序列化到OutputStream中。

      public static void writeTaskToStream(TaskProtos.Task task,

      OutputStream outputStream)

      throws IOException {

      task.writeTo(outputStream);

      }

      protobuf主要的優點是它比JSON消耗的內存少,而且讀寫速度更快。protobuf對象還是不可變的,如果要確保對象的值在整個生命周期中保持不變,該特性會非常有用。

      Protocol Buffers 2.6.1 full source:?protobuf-2.6.1.tar.gz?(MD5: f3916ce13b7fcb3072a1fa8cf02b2423)

      Protocol Compiler 2.6.1 binary for windows:?protoc-2.6.1-win32.zip?(MD5: b057f86ef83835010bb227eb2d82de04)

      C++ Java

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:解析PDF和圖片如何快速轉為Word,畢業季,輕松搞定論文(word圖片怎么轉PDF)
      下一篇:企業私有云建設指南》一3.2.7 如何開始業務的上云遷移
      相關文章
      亚洲精品9999久久久久无码| 亚洲国产精品久久久久网站| 亚洲蜜芽在线精品一区| 一区国严二区亚洲三区| 亚洲AV综合永久无码精品天堂| 亚洲国产精品一区二区三区在线观看| 亚洲综合在线视频| 日木av无码专区亚洲av毛片| 亚洲va无码专区国产乱码| 亚洲国产精品无码一线岛国| 亚洲色成人中文字幕网站| 亚洲精品无码成人AAA片| 国产亚洲精品va在线| 亚洲成A人片在线观看无码不卡 | 91亚洲国产成人久久精品| 亚洲视频在线观看网址| 亚洲av色福利天堂| 亚洲伦理一区二区| 久久久久亚洲AV片无码下载蜜桃| 亚洲午夜视频在线观看| 亚洲一区二区三区夜色| 亚洲精品视频在线免费| 亚洲男人天堂影院| 亚洲国产理论片在线播放| 亚洲六月丁香婷婷综合| 亚洲精品天堂在线观看| 亚洲日韩一区二区三区| 精品久久久久久久久亚洲偷窥女厕| 国产精品无码亚洲精品2021| 亚洲国产精品无码久久九九 | 亚洲美女高清一区二区三区 | 亚洲经典在线中文字幕| 亚洲中文无码av永久| 亚洲免费视频播放| 久久噜噜噜久久亚洲va久| 亚洲精品乱码久久久久久按摩 | 亚洲AV无码专区国产乱码4SE| 亚洲乱码国产一区网址| 春暖花开亚洲性无区一区二区 | 亚洲日韩av无码中文| 内射少妇36P亚洲区|