1 Introduction
Protocol Buffers是在網(wǎng)絡通訊或者數(shù)據(jù)存儲時用到的一種語言無關、平臺無關、可擴展的序列化結構數(shù)據(jù)格式。類似于XML,不過比XML更小、更快、更簡單。
你首先要定義自己想要的數(shù)據(jù)結構(在 .proto 文件中定義數(shù)據(jù)結構信息),經(jīng)過Protocol Buffers 編譯后生成.java代碼文件,就可以根據(jù).java里的數(shù)據(jù)對象來進行讀寫操作。
下面談談在Android開發(fā)中如何接入protocol buffers。
2 JavaNano
protocol buffers支持多種語言環(huán)境下的使用。Java雖然是Android開發(fā)的絕對主流開發(fā)語言,但是protobuf還是針對Android開發(fā)這種性能資源有限的系統(tǒng)專門提供了一個JavaNano版本(而不是直接使用protobuf的Java版本進行編譯),在代碼量和運行時開銷上都比較友好(詳細介紹點這)。
3 Usage
3.1 Defining A Message Type
即編寫.protol文件,通常這部分工作由后臺完成,畢竟是后端來決定在網(wǎng)絡連接中,傳輸什么字段給客戶端或者客戶端提交什么字段給服務器。類似于定義json的字段結構。
示例:
syntax = "proto3";//指定protocol buffer版本
message SearchRequest {//這是一個message的定義,包含了三個屬性,每個屬性有一個字段名和類型
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
message SearchResponse {
...//一些字段的定義
}
3.2 What's Generated From Your .proto?
運行protocol buffer 編譯器去編譯.proto文件,可以根據(jù)你選擇的語言生成相應的代碼文件。
編譯Java語言版本的時候,編譯器會為每一個message生成.java
類文件,每個類還對應一個Builder
提供給開發(fā)者去構建實例。值得注意的是,我們在Android上使用的protobuf JavaNano與protobuf Java相比,去除了一些復雜的特性。比如剛剛提到的Builder,還有Descriptors都被去掉。同時通過直接訪問類的public成員變量的方式取代了set/get方法。
3.3 Compile on Windows
在Windwos上編譯.protol文件十分簡單。
- protocol 編譯器是用C++寫的,C++開發(fā)者可以在github google/protobuf下載源碼編譯出來。對于非C++開發(fā)者,也可以直接下載現(xiàn)成的編譯器protoc-3.0.0-beta-3-win32.zip。(這里獲取各個OS下的編譯器。)
- 解壓下載好的protoc-3.0.0-beta-3-win32.zip,將解壓后的目錄名添加到Windows的PATH環(huán)境變量。
- 調(diào)出Windows的命令行,輸入
protoc --version
,出現(xiàn)以下畫面,說明編譯器可以使用了。
protoc --version - 生成Java類
protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/message.proto
上面的$SRC_DIR
表示存放message.proto文件的路徑,$DST_DIR
表示生成java類存放的路徑。-I=$SRC_DIR
可以省略,如果message.proto文件與protoc.exe在同一個目錄下。
例如:
protoc -I=D:\ --javanano_out=E:\ D:\message.proto
這里我的message.proto文件放在D盤,指定編譯生成的java類放到E盤。
可以看到編譯出來的java類文件已經(jīng)安靜地躺在我的E盤中。這個類就和我們平時使用的Bean類的用法一毛一樣了。
3.4 Compile Options
有一些常用的編譯選項可以在message.proto文件中預先定義:
option java_package = "com.android.develop"; //指定java類的包名
option java_multiple_files = true; //message.proto文件中的每個message都生成一個對應的.java。設為fasle時只導出一個.java類文件,所有message都以內(nèi)部類的形式出現(xiàn)
option java_outer_classname = "MessageBean";//當只生成一個.java文件時,指定類名, java_multiple_files =true時,這個屬性無效。
message SearchRequest {
int32 page_number = 2;
int32 result_per_page = 3;
}
message OtherMessages{
.......
}
命令行編譯時,還有以下選項可以用:
-
optional_field_style={default,accessors,reftypes} (default: default)
設置成員變量的訪問方式。default就是public直接訪問,accessors就是set/get方法,reftypes使用基本類型的包裝類來定義字段類型(例如Interger代替原本的int,空值為null,可以避免空值==0時的歧義。)。 enum_style={c,java} (default: c)
-
parcelable_messages={true,false} (default: false)
開啟了這個選項后,編譯生成的java類自動實現(xiàn)Parcelable接口,最常用的Intent傳值就可以直接使用這些對象。
使用示例:
protoc --javanano_out=optional_field_style=accessors,parcelable_messages=true:d:\ D:\protoc-3.0.0-beta-3-win32\message.proto
這時,編譯出來的java類,就是通過set/get方法訪問private的成員變量,同時也已經(jīng)實現(xiàn)了Parcelable接口。
3.5 The Protocol Buffer API
每一個編譯后生成的protobuf Message類都提供了方法去讀寫二進制格式的數(shù)據(jù)。
-
byte[] toByteArray();
序列化message,并返回一個包含了message的原始數(shù)據(jù)的byte數(shù)組。 -
static SearchRequest parseFrom(byte[] data);
從一個給定的byte數(shù)組中,反序列化出來一個message對象。 -
void writeTo(OutputStream output);
序列化message,并寫到一個OutputStream
上。 -
static SearchRequest parseFrom(InputStream input);
從一個InputStream
讀取并反序列化出message對象。
3.6 Protocol Buffers and O-O Design
官方推薦使用包裝類的形式來對生成的ProtoBuf Message java類進行封裝及擴展,而不是繼承它們。因為繼承并重寫父類方法的時候將會破壞ProtoBuf Message類的內(nèi)在機制。
You should never add behaviour to the generated classes by inheriting from them. This will break internal mechanisms and is not good object-oriented practice anyway.