序列化
計(jì)算機(jī)中最原始的數(shù)據(jù)就是0和1,有電才能驅(qū)動(dòng)計(jì)算機(jī)運(yùn)行,1和0對(duì)應(yīng)電的正負(fù)暂氯,易經(jīng)有太極生兩儀,兩儀生四象存筏,四象生八卦,八卦衍萬物味榛,所以0和1可衍生萬物椭坚,萬物皆對(duì)象,那這對(duì)象怎么保存搏色,最簡(jiǎn)單就是保存0和1就行善茎,對(duì)象轉(zhuǎn)變成0和1就是序列化,0和1轉(zhuǎn)成對(duì)象就是反序列化
存儲(chǔ)數(shù)據(jù)和傳輸數(shù)據(jù)
簡(jiǎn)單舉的例子:
存儲(chǔ):在java中的面向?qū)ο缶幊讨衅到危瑪?shù)據(jù)通常是用一個(gè)字段表示垂涯,如名字name=張三,字段為name,value為張三略吨,這個(gè)字段在一個(gè)類對(duì)象中集币,這個(gè)對(duì)象是在程序運(yùn)行中創(chuàng)建在內(nèi)存中的考阱,持久保存"張三"就需要把這個(gè)對(duì)象序列化保存在硬盤上翠忠。
傳輸:傳輸和存儲(chǔ)是一樣的,上面說的存儲(chǔ)就是內(nèi)存?zhèn)饔脖P乞榨,另一種場(chǎng)景秽之,兩臺(tái)計(jì)算機(jī)之間傳輸數(shù)據(jù),序列化后的數(shù)據(jù)是0和1的2進(jìn)制串吃既,比如兩臺(tái)計(jì)算機(jī)連著一根金屬線考榨,在這跟金屬線上通電與斷電表示1或0,這樣另一臺(tái)計(jì)算機(jī)得到2進(jìn)制串后進(jìn)行反序列化鹦倚,就可以得到內(nèi)容相同的對(duì)象了河质。
序列化故名思意就是按序排列變化,"序"就是某種協(xié)議,按這種協(xié)議排列變成另一種形態(tài)
簡(jiǎn)單的概括
- 序列化: 主要用于網(wǎng)絡(luò)傳輸掀鹅,數(shù)據(jù)持久化散休,一般序列化也稱為編碼(Encode)
- 反序列化: 主要用于從網(wǎng)絡(luò),磁盤上讀取字節(jié)數(shù)組還原成原始對(duì)象乐尊,一般反序列化也稱為解碼(Decode)
以上得知序列化是將一個(gè)對(duì)象的數(shù)據(jù)保存或傳輸戚丸,怎么樣使這個(gè)對(duì)象能被序列化?
Serializable:可序列化的
Serializable是一個(gè)空接口扔嵌,中文意思使可序列化的限府,意思使實(shí)現(xiàn)了這個(gè)接口的javaBean類可以序列化成2進(jìn)制串,使用ObjectOutputStream 序列化痢缎,ObjectInputStream 反序列化胁勺。
public class SPerson implements Serializable {
//serialVersionUID 是 Java 為每個(gè)序列化類產(chǎn)生的版本標(biāo)識(shí),可用來保證在反序列時(shí)独旷,發(fā)送方發(fā)送的和接受方接收的是可兼容的對(duì)象姻几。如果接收方接收的類的 //serialVersionUID 與發(fā)送方發(fā)送的 serialVersionUID 不一致,進(jìn)行反序列時(shí)會(huì)拋出 InvalidClassException
//
private static final long serialVersionUID = 2088008379212083287L;
private String name;
private int age;
}
public class SerializeableTest {
public static void main(String[] args){
SPerson sPerson1 =new SPerson();
sPerson1.setName("張三");
sPerson1.setAge(18);
System.out.println("serializable="+ sPerson1.getName()+"--"+ sPerson1.getAge());
try {
ByteArrayOutputStream baos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(baos);
oos.writeObject(sPerson1);
byte[] bs2= baos.toByteArray();
System.out.println("serializable序列化后="+ Arrays.toString(bs2));
ObjectInputStream ojis=new ObjectInputStream(new ByteArrayInputStream(bs2));
SPerson sPerson2 = (SPerson) ojis.readObject();
System.out.println("serializable反序列化="+ sPerson2.getName()+"--"+ sPerson2.getAge());
} catch (Exception e) {
e.printStackTrace();
}
}
}
打印結(jié)果:
serializable=張三--18
serializable序列化后=[-84, -19, 0, 5, 115, 114, 0, 39, 99, 111, 109, 46, 100, 98, 102, 46, 106, 97, 118, 97, 115, 116, 117, 100, 121, 46, 115, 101, 114, 105, 97, 108, 105, 122, 97, 116, 105, 111, 110, 46, 83, 80, 101, 114, 115, 111, 110, 28, -6, 24, -114, -30, -7, -80, 87, 2, 0, 2, 73, 0, 3, 97, 103, 101, 76, 0, 4, 110, 97, 109, 101, 116, 0, 18, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 120, 112, 0, 0, 0, 18, 116, 0, 6, -27, -68, -96, -28, -72, -119]
serializable反序列化=張三--18
序列化后的文件直接用sublime打開如下
aced 0005 7372 002e 636f 6d2e 7a65 726f
2e73 6572 6961 6c69 7a61 626c 6564 656d
6f2e 7365 7269 616c 697a 6162 6c65 2e53
7475 6465 6e74 e2d9 8cd7 833d f19e 0200
...
- AC ED: STREAM_MAGIC. 聲明使用了序列化協(xié)議.
- 00 05: STREAM_VERSION. 序列化協(xié)議版本.
- 0x73: TC_OBJECT. 聲明這是一個(gè)新的對(duì)象.
- 0x72: TC_CLASSDESC. 聲明這里開始一個(gè)新Class势告。
- 00 2e: Class名字的長度.
不容易發(fā)現(xiàn)的問題:
SPerson sPerson1 =new SPerson();
sPerson1.setName("張三");
sPerson1.setAge(18);
ByteArrayOutputStream baos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(baos);
oos.writeObject(sPerson1);
sPerson1.setName("李四");
//oos.writeUnshared(sPerson1);不共享
oos.writeObject(sPerson1);共享
ObjectInputStream ojis=new ObjectInputStream(new ByteArrayInputStream(bs2));
SPerson sPerson2 = (SPerson) ojis.readObject();
SPerson sPerson3 = (SPerson) ojis.readObject();
//以上是想保存一個(gè)李四18蛇捌,設(shè)置李四后調(diào)用oos.writeObject(sPerson1) 反序列化出來的是兩個(gè)張三,得用oos.writeUnshared(sPerson1)咱台;
Parcelable 可打包的
Parcelable是Android為我們提供的序列化的接口,Parcelable相對(duì)于Serializable的使用相對(duì)復(fù)雜一些,但Parcelable的效率相對(duì)Serializable也高很多,Parcelable vs Serializable 號(hào)稱快10倍的效率
Parcelable是Android SDK提供的络拌,它是基于內(nèi)存的,由于內(nèi)存讀寫速度高于硬盤回溺,因此Android中的跨進(jìn)程對(duì)象的傳遞一般使用Parcelable
public class PPerson implements Parcelable {
private String name;
private int age;
//反序列化時(shí)構(gòu)建對(duì)象春贸,序列化的數(shù)據(jù)包在Parcel中,Parcel就是包裹的意思
protected PPerson(Parcel in) {
name = in.readString();
age = in.readInt();
}
//構(gòu)建器遗遵,構(gòu)造一個(gè)PPerson對(duì)象和PPerson數(shù)組對(duì)象
public static final Creator<PPerson> CREATOR = new Creator<PPerson>() {
@Override
public PPerson createFromParcel(Parcel in) {
return new PPerson(in);
}
@Override
public PPerson[] newArray(int size) {
return new PPerson[size];
}
};
//描述包含在這個(gè) Parcelable 實(shí)例的封送表示中的特殊對(duì)象的種類萍恕。例如,如果對(duì)象將在 {@link writeToParcel(Parcel, int)} 的輸出中包含文件描述符车要,則此方法的返回值必須包含 {@link CONTENTS_FILE_DESCRIPTOR} 位允粤。 @return 一個(gè)位掩碼,指示由此 Parcelable 對(duì)象實(shí)例編組的一組特殊對(duì)象類型翼岁。
@Override
public int describeContents() {
return 0;
}
//將數(shù)據(jù)打包到Parcel
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
}
Android也是基于linux类垫,進(jìn)程之間內(nèi)存不共享,因此進(jìn)程之間的信息傳遞需要序列化與反序列化琅坡,通常在四大組件之間傳遞對(duì)象需要將對(duì)象序列化悉患,啟動(dòng)一個(gè)Activity交給AMS完成,AMS全名ActivityManagerService,是系統(tǒng)服務(wù)榆俺,是另一個(gè)進(jìn)程售躁,這里就得序列化了坞淮,關(guān)于啟動(dòng)四大組件是比較復(fù)雜的流程,可以看看下面的文章
startActivity啟動(dòng)過程分析:http://gityuan.com/2016/03/12/start-activity/
Android中Intent/Bundle的通信原理及大小限制
速度快陪捷,比如兩個(gè)Activity之間傳遞碾盐,Activity用于頁面展示,速度一定得快揩局,其次數(shù)據(jù)量也不能過大毫玖,大數(shù)據(jù)傳輸,肯定會(huì)導(dǎo)致速度變慢凌盯,能感覺頁面明顯切換得慢了付枫,數(shù)據(jù)過大時(shí)會(huì)報(bào)一個(gè)錯(cuò)誤,
Intent 中的 Bundle 是使用 Binder 機(jī)制進(jìn)行數(shù)據(jù)傳送的驰怎。能使用的 Binder 的緩沖區(qū)是有大小限制的(有些手機(jī)是 2 M)阐滩,而一個(gè)進(jìn)程默認(rèn)有 16 個(gè) Binder 線程,所以一個(gè)線程能占用的緩沖區(qū)就更小了( 有人以前做過測(cè)試县忌,大約一個(gè)線程可以占用 128 KB)掂榔。所以當(dāng)你看到 The Binder transaction failed because it was too large 這類 TransactionTooLargeException 異常時(shí),你應(yīng)該知道怎么解決了
怎么實(shí)現(xiàn)跨進(jìn)程通信症杏,通過AIDL快速生成代碼,下面是生成的部分代碼
@Override public void addPerson(com.dbf.studyandtest.myaidl.Person person) throws android.os.RemoteException
{
//先獲得傳入和傳出的打包對(duì)象Parcel装获,看到obtain應(yīng)該是用了享元模式,與handler傳消息的message一樣厉颤,對(duì)象頻繁使用穴豫,減少new,重復(fù)利用已經(jīng)new好的對(duì)象
android.os.Parcel _data = android.os.Parcel.obtain();//傳入數(shù)據(jù),就是給另一個(gè)進(jìn)程的數(shù)據(jù)
android.os.Parcel _reply = android.os.Parcel.obtain();//傳出數(shù)據(jù)逼友,就是另一個(gè)進(jìn)程回傳的數(shù)據(jù)
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((person!=null)) {
_data.writeInt(1);
person.writeToParcel(_data, 0);//將數(shù)據(jù)寫入Parcel精肃,可以看到我們實(shí)現(xiàn)Parcelable的類,調(diào)用的是native方法
}
else {
_data.writeInt(0);
}
boolean _status = mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().addPerson(person);
return;
}
_reply.readException();
if ((0!=_reply.readInt())) {
person.readFromParcel(_reply);//從Parcel讀出數(shù)據(jù)帜乞,可以看到我們實(shí)現(xiàn)Parcelable的類司抱,也是調(diào)用native方法
}
}
finally {
//最后釋放
_reply.recycle();
_data.recycle();
}
}
linux中分用戶空間和內(nèi)核空間,先從用戶空間其中一個(gè)進(jìn)程cp到內(nèi)核空間黎烈,再從內(nèi)核空間映射到用戶空間的另一個(gè)進(jìn)程习柠,想要達(dá)到這個(gè)目的要繼承Binder實(shí)現(xiàn)IInterface,代碼形式都是固定的怨喘,所以有了AIDL,Android Interface Definition Language,即Android接口定義語言
Parcelable與Serializable的性能比較
Serializable性能分析
Serializable是Java中的序列化接口津畸,其使用起來簡(jiǎn)單但開銷較大(因?yàn)镾erializable在序列化過程中使用了反射機(jī)制,故而會(huì)產(chǎn)生大量的臨時(shí)變量必怜,從而導(dǎo)致頻繁的GC),并且在讀寫數(shù)據(jù)過程中后频,它是通過IO流的形式將數(shù)據(jù)寫入到硬盤或者傳輸?shù)骄W(wǎng)絡(luò)上梳庆。
Parcelable性能分析
Parcelable則是以IBinder作為信息載體暖途,在內(nèi)存上開銷比較小,因此在內(nèi)存之間進(jìn)行數(shù)據(jù)傳遞時(shí)膏执,推薦使用Parcelable,而Parcelable對(duì)數(shù)據(jù)進(jìn)行持久化或者網(wǎng)絡(luò)傳輸時(shí)操作復(fù)雜驻售,一般這個(gè)時(shí)候推薦使用Serializable。
性能比較總結(jié)描述
首先Parcelable的性能要強(qiáng)于Serializable的原因我需要簡(jiǎn)單的闡述一下
- 在內(nèi)存的使用中,前者在性能方面要強(qiáng)于后者
- 后者在序列化操作的時(shí)候會(huì)產(chǎn)生大量的臨時(shí)變量,(原因是使用了反射機(jī)制)從而導(dǎo)致GC的頻繁調(diào)用,因此在性能上會(huì)稍微遜色
- Parcelable是以Ibinder作為信息載體的.在內(nèi)存上的開銷比較小,因此在內(nèi)存之間進(jìn)行數(shù)據(jù)傳遞的時(shí)候,Android推薦使用Parcelable,既然是內(nèi)存方面比價(jià)有優(yōu)勢(shì),那么自然就要優(yōu)先選擇.
- 在讀寫數(shù)據(jù)的時(shí)候,Parcelable是在內(nèi)存中直接進(jìn)行讀寫,而Serializable是通過使用IO流的形式將數(shù)據(jù)讀寫入在硬盤上.
但是:雖然Parcelable的性能要強(qiáng)于Serializable,但是仍然有特殊的情況需要使用Serializable,而不去使用Parcelable,因?yàn)镻arcelable無法將數(shù)據(jù)進(jìn)行持久化,因此在將數(shù)據(jù)保存在磁盤的時(shí)候,仍然需要使用后者,因?yàn)榍罢邿o法很好的將數(shù)據(jù)進(jìn)行持久化.(原因是在不同的Android版本當(dāng)中,Parcelable可能會(huì)不同,因此數(shù)據(jù)的持久化方面仍然是使用Serializable)
Protobuf
- 序列化數(shù)據(jù)非常簡(jiǎn)潔更米,緊湊欺栗,與 XML 相比,其序列化之后的數(shù)據(jù)量約為 1/3 到 1/10征峦。
- 解析速度非吵偌福快,比對(duì)應(yīng)的 XML 快約 20-100 倍栏笆。
一类腮、配置protobuf
插件github地址:https://github.com/google/protobuf-gradle-plugin
1.在項(xiàng)目根目錄的build.gradle中配置:
dependencies {
classpath 'com.android.tools.build:gradle:4.1.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.17'//protobuf插件
}
2.在模塊中的build.gradle中配置:
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'com.google.protobuf'//protobuf插件
android {
...
sourceSets {
main {
java {
srcDir 'src/main/java'
}
proto {
srcDir 'src/main/proto'//添加protobuf目錄,在里面編寫需要序列化的文件
}
}
}
}
protobuf {
//配置protoc編譯器
protoc {
artifact = 'com.google.protobuf:protoc:3.8.0'//這里的版本跟斜面依賴的版本一致
}
//這里配置生成目錄蛉加,編譯后會(huì)在build的目錄下生成對(duì)應(yīng)的java文件
generateProtoTasks {
all().each { task ->
task.builtins {
remove java
}
task.builtins {
java {}
}
}
}
}
dependencies {
implementation 'com.google.protobuf:protobuf-java:3.8.0'
}
3.Settings Plugins 搜索安裝 Protobuf Support 插件蚜枢,裝好后可以識(shí)別出.proto文件
二、編寫protobuf
.proto文件編寫 github地址:https://github.com/protocolbuffers/protobuf
proto3本版本java:https://developers.google.com/protocol-buffers/docs/proto3
例子:
syntax = "proto3";//版本 之前有proto2
//package tutorial;
/**java_multiple_files = true
選項(xiàng)可以生成單獨(dú)的.java每個(gè)生成的類的文件
(而不是.java為包裝類生成單個(gè)文件的遺留行為针饥,使用包裝類作為外部類厂抽,并將所有其他類嵌套在包裝類中)。
*/
option java_multiple_files = true;
/** java_package指定生成的類應(yīng)該使用的 Java 包名稱丁眼。
如果您沒有明確指定修肠,它只是匹配package聲明給出的包名,
但這些名稱通常不是合適的 Java 包名(因?yàn)樗鼈兺ǔ2灰杂蛎_頭)户盯。
*/
option java_package = "com.dbf.studyandtest.proto";
/**java_outer_classname選項(xiàng)定義將表示此文件的包裝類的類名嵌施。
如果你不java_outer_classname明確給出a ,它會(huì)通過將文件名轉(zhuǎn)換為大寫駝峰來生成莽鸭。
例如吗伤,默認(rèn)情況下,“my_proto.proto”將使用“MyProto”作為包裝類名稱硫眨。
*/
option java_outer_classname = "_Person";
message Person{
/**每個(gè)元素上的“= 1”足淆、“= 2”標(biāo)記標(biāo)識(shí)字段在二進(jìn)制編碼中使用的唯一“標(biāo)簽”。
標(biāo)記數(shù)字 1-15 需要比更高數(shù)字少一個(gè)字節(jié)來編碼礁阁,因此作為一種優(yōu)化巧号,您可以決定將這些標(biāo)記用于常用或重復(fù)的元素,
而將標(biāo)記 16 和更高的標(biāo)記用于不太常用的可選元素姥闭。重復(fù)字段中的每個(gè)元素都需要重新編碼標(biāo)簽編號(hào)丹鸿,因此重復(fù)字段特別適合這種優(yōu)化。
*/
//指定字段的類型 定義字段的編號(hào)棚品,在Protocol Buffers中靠欢,字段的編號(hào)非常重要廊敌,字段名僅僅是作為參考和生成代碼用。需要注意的是字段的編號(hào)區(qū)間范圍门怪,其中19000 ~ 19999被Protocol Buffers作為保留字段
string name = 1;
int32 age = 2;
// message Home{
// string address = 1;
// }
// Home home = 3;
}
//message Family {
// Person person = 1;
//}
Make project 一下自動(dòng)生成代碼
/**
* Protobuf type {@code Person}
*/
public final class Person extends com.google.protobuf.GeneratedMessageV3 implements PersonOrBuilder {
//省略600多行代碼
}
與Serializable對(duì)比
public static void main(String[] args) throws Exception{
Person person = Person.newBuilder().setName("張三").setAge(18).build();
System.out.println("protobuf="+person.getName()+"--"+person.getAge());
//序列化
byte[] bs=person.toByteArray();
System.out.println("protobuf序列化后="+Arrays.toString(bs));
Person person2=Person.parseFrom(bs);
System.out.println("protobuf反序列化="+person2.getName()+"--"+person2.getAge());
System.out.println("\n----------------分割線-------------\n");
SPerson sPerson1 =new SPerson();
sPerson1.setName("張三");
sPerson1.setAge(18);
System.out.println("serializable="+ sPerson1.getName()+"--"+ sPerson1.getAge());
try {
ByteArrayOutputStream baos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(baos);
oos.writeObject(sPerson1);
byte[] bs2= baos.toByteArray();
System.out.println("serializable序列化后="+Arrays.toString(bs2));
ObjectInputStream ojis=new ObjectInputStream(new ByteArrayInputStream(bs2));
SPerson sPerson2 = (SPerson) ojis.readObject();
System.out.println("serializable反序列化="+ sPerson2.getName()+"--"+ sPerson2.getAge());
} catch (Exception e) {
e.printStackTrace();
}
}
打印結(jié)果:
protobuf=張三--18
protobuf序列化后=[10, 6, -27, -68, -96, -28, -72, -119, 16, 18]
protobuf反序列化=張三--18
----------------分割線-------------
serializable=張三--18
serializable序列化后=[-84, -19, 0, 5, 115, 114, 0, 39, 99, 111, 109, 46, 100, 98, 102, 46, 106, 97, 118, 97, 115, 116, 117, 100, 121, 46, 115, 101, 114, 105, 97, 108, 105, 122, 97, 116, 105, 111, 110, 46, 83, 80, 101, 114, 115, 111, 110, 28, -6, 24, -114, -30, -7, -80, 87, 2, 0, 2, 73, 0, 3, 97, 103, 101, 76, 0, 4, 110, 97, 109, 101, 116, 0, 18, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 120, 112, 0, 0, 0, 18, 116, 0, 6, -27, -68, -96, -28, -72, -119]
serializable反序列化=張三--18
.proto Type | Notes | C++ Type | Java/Kotlin Type[1] | Python Type[3] | Go Type | Ruby Type | C# Type | PHP Type | Dart Type |
---|---|---|---|---|---|---|---|---|---|
double | double | double | float | float64 | Float | double | float | double | |
float | float | float | float | float32 | Float | float | float | double | |
int32 | 使用可變長度編碼骡澈。 編碼負(fù)數(shù)效率低下 - 如果您的字段可能具有負(fù)值,請(qǐng)改用 sint32掷空。 | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int |
int64 | 使用可變長度編碼肋殴。 編碼負(fù)數(shù)效率低下 - 如果您的字段可能具有負(fù)值,請(qǐng)改用 sint64坦弟。 | int64 | long | int/long[4] | int64 | Bignum | long | integer/string[6] | Int64 |
uint32 | 使用可變長度編碼护锤。 | uint32 | int[2] | int/long[4] | uint32 | Fixnum or Bignum (as required) | uint | integer | int |
uint64 | 使用可變長度編碼。 | uint64 | long[2] | int/long[4] | uint64 | Bignum | ulong | integer/string[6] | Int64 |
sint32 | 使用可變長度編碼减拭。 有符號(hào)整數(shù)值蔽豺。 這些比常規(guī) int32 更有效地編碼負(fù)數(shù)。 | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int |
sint64 | 使用可變長度編碼拧粪。 有符號(hào)整數(shù)值修陡。 這些比常規(guī) int64 更有效地編碼負(fù)數(shù)。 | int64 | long | int/long[4] | int64 | Bignum | long | integer/string[6] | Int64 |
fixed32 | 總是四個(gè)字節(jié)可霎。 如果值通常大于 228魄鸦,則比 uint32 更有效。 | uint32 | int[2] | int/long[4] | uint32 | Fixnum or Bignum (as required) | uint | integer | int |
fixed64 | 總是八個(gè)字節(jié)癣朗。 如果值通常大于 256拾因,則比 uint64 更有效。 | uint64 | long[2] | int/long[4] | uint64 | Bignum | ulong | integer/string[6] | Int64 |
sfixed32 | 總是四個(gè)字節(jié)旷余。 | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int |
sfixed64 | 總是八個(gè)字節(jié)绢记。 | int64 | long | int/long[4] | int64 | Bignum | long | integer/string[6] | Int64 |
bool | bool | boolean | bool | bool | TrueClass/FalseClass | bool | boolean | bool | |
string | 字符串必須始終包含 UTF-8 編碼或 7 位 ASCII 文本,并且長度不能超過 232正卧。 | string | String | str/unicode[5] | string | String (UTF-8) | string | string | String |
bytes | 可以包含不超過 232 的任意字節(jié)序列蠢熄。 | string | ByteString | str (Python 2) bytes (Python 3) | []byte | String (ASCII-8BIT) | ByteString | string |