引言
1. Android性能優(yōu)化篇之內存優(yōu)化--內存泄漏
2.Android性能優(yōu)化篇之內存優(yōu)化--內存優(yōu)化分析工具
3.Android性能優(yōu)化篇之UI渲染性能優(yōu)化
4.Android性能優(yōu)化篇之計算性能優(yōu)化
5.Android性能優(yōu)化篇之電量優(yōu)化(1)——電量消耗分析
6.Android性能優(yōu)化篇之電量優(yōu)化(2)
7.Android性能優(yōu)化篇之網(wǎng)絡優(yōu)化
8.Android性能優(yōu)化篇之Bitmap優(yōu)化
9.Android性能優(yōu)化篇之圖片壓縮優(yōu)化
10.Android性能優(yōu)化篇之多線程并發(fā)優(yōu)化
11.Android性能優(yōu)化篇之數(shù)據(jù)傳輸效率優(yōu)化
12.Android性能優(yōu)化篇之程序啟動時間性能優(yōu)化
13.Android性能優(yōu)化篇之安裝包性能優(yōu)化
14.Android性能優(yōu)化篇之服務優(yōu)化
介紹
數(shù)據(jù)的序列化是程序代碼里面必不可少的組成部分,這篇我們就來一起看看序列化相關的知識蜜笤。
數(shù)據(jù)序列化的行為可能發(fā)生在數(shù)據(jù)傳遞過程中的任何階段聘惦,例如網(wǎng)絡傳輸减途,不同進程間數(shù)據(jù)傳遞辟拷,不同類之間的參數(shù)傳遞谒出,把數(shù)據(jù)存儲到磁盤上等等。
什么是序列化和反序列化?
簡單來說就是將對象轉換為可以傳輸?shù)亩M制流(二進制序列)的過程,這樣我們就可以通過序列化,轉化為可以在網(wǎng)絡傳輸或者保存到本地的流(序列),從而進行傳輸數(shù)據(jù) ,那反序列化就是從二進制流(序列)轉化為對象的過程.
常用的序列化有哪幾種?
(1).Serializable
Serializable是我們平時使用比較多的祟绊,但是這種傳統(tǒng)的做法效率不高讲坎,實施的過程會消耗更多的內存货邓。
使用
public class CompanyInfo implements Serializable {
序列化
out = new FileOutputStream(mSerializeFile);
CompanyInfo companyInfo = new CompanyInfo(1001, "常州網(wǎng)絡有限公司");
objectOutputStream = new ObjectOutputStream(out);
objectOutputStream.writeObject(companyInfo);
反序列化
fIn = new FileInputStream(mSerializeFile);
objectInputStream = new ObjectInputStream(fIn);
Object object
= objectInputStream.readObject();
if(object instanceof CompanyInfo){
CompanyInfo info = (CompanyInfo) object;
}
(2).Parcelable
Parcelable是Android為我們提供的序列化的接口,Parcelable相對于Serializable的使用相對復雜一些,但Parcelable的效率相對Serializable也高很多
使用
public class PersonInfo implements Parcelable
序列化
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(name);
dest.writeString(love);
}
反序列化
protected PersonInfo(Parcel in) {
id = in.readInt();
name = in.readString();
love = in.readString();
}
public static final Creator<PersonInfo> CREATOR = new Creator<PersonInfo>() {
@Override
public PersonInfo createFromParcel(Parcel in) {
return new PersonInfo(in);
}
@Override
public PersonInfo[] newArray(int size) {
return new PersonInfo[size];
}
};
(3).Gson
GSON庫來處理這個序列化的問題,不僅僅執(zhí)行速度更快四濒,內存的使用效率也更高换况。
下面介紹三個數(shù)據(jù)序列化的候選方案:
- Protocal Buffers:強大,靈活盗蟆,但是對內存的消耗會比較大戈二,并不是移動終端上的最佳選擇。
- Nano-Proto-Buffers:基于Protocal喳资,為移動終端做了特殊的優(yōu)化觉吭,代碼執(zhí)行效率更高,內存使用效率更佳仆邓。
FlatBuffers:這個開源庫最開始是由Google研發(fā)的鲜滩,專注于提供更優(yōu)秀的性能。
上面這些方案在性能方面的數(shù)據(jù)對比如下圖所示:
FlatBuffers 幾乎從空間和時間復雜度上完勝其他技術节值。
FlatBuffers 是一個開源的跨平臺數(shù)據(jù)序列化庫徙硅,可以應用到幾乎任何語言(C++, C#, Go, Java, JavaScript, PHP, Python),最開始是 Google 為游戲或者其他對性能要求很高的應用開發(fā)的搞疗。
FlatBuffers
1.FlatBuffers優(yōu)點
(1).直接讀取序列化數(shù)據(jù)嗓蘑,而不需要解析(Parsing)或者解包(Unpacking):FlatBuffer 把數(shù)據(jù)層級結構保存在一個扁平化的二進制緩存(一維數(shù)組)中,同時能夠保持直接獲取里面的結構化數(shù)據(jù),而不需要解析桩皿,并且還能保證數(shù)據(jù)結構變化的前后向兼容豌汇。
(2).高效的內存使用和速度:FlatBuffer 使用過程中,不需要額外的內存业簿,幾乎接近原始數(shù)據(jù)在內存中的大小瘤礁。
(3).靈活:數(shù)據(jù)能夠前后向兼容,并且能夠靈活控制你的數(shù)據(jù)結構梅尤。
(4).很少的代碼侵入性:使用少量的自動生成的代碼即可實現(xiàn)柜思。
(5).強數(shù)據(jù)類性,易于使用巷燥,跨平臺赡盘,幾乎語言無關。
官方提供了一個性能對比表
在做 Android 開發(fā)的時候缰揪,JSON 是最常用的數(shù)據(jù)序列化技術陨享。我們知道,JSON 的可讀性很強钝腺,但是序列化和反序列化性能卻是最差的抛姑。解析的時候,JSON 解析器首先艳狐,需要在內存中初始化一個對應的數(shù)據(jù)結構定硝,這個事件經(jīng)常會消耗 100ms ~ 200ms2;解析過程中毫目,要產(chǎn)生大量的臨時變量蔬啡,造成 Java 虛擬機的 GC 和內存抖動,解析 20KB 的數(shù)據(jù)镀虐,大概會消耗 100KB 的臨時內存2箱蟆。FlatBuffers 就解決了這些問題。
2.FlatBuffers缺點
(1).FlatBuffers 需要生成代碼刮便,對代碼有侵入性
(2).數(shù)據(jù)序列化沒有可讀性空猜,不方便 Debug;
(3).構建 FlatBuffers 對象比較麻煩恨旱,不直觀抄肖,特別是如果對象比較復雜情況下需要寫大段的代碼;
(4).數(shù)據(jù)的所有內容需要使用 Schema 嚴格定義窖杀,靈活性不如 JSON漓摩。
3.FlatBuffers原理
官方文檔的介紹,F(xiàn)latBuffers 就像它的名字所表示的一樣入客,就是把結構化的對象管毙,用一個扁平化(Flat)的緩沖區(qū)保存腿椎,簡單的來說就是把內存對象數(shù)據(jù),保存在一個一維的數(shù)組中夭咬。借用 Facebook 文章2的一張圖如下:
可見啃炸,F(xiàn)latBuffers 保存在一個 byte 數(shù)組中,有一個“支點”指針(pivot point)以此為界卓舵,存儲的內容分為兩個部分:元數(shù)據(jù)和數(shù)據(jù)內容南用。其中元數(shù)據(jù)部分就是數(shù)據(jù)在前面,其長度等于對象中的字段數(shù)量掏湾,每個 byte 保存對應字段內容在數(shù)組中的索引(從支點位置開始計算)裹虫。
如圖,上面的 Person 對象第一個字段是 name融击,其值的索引位置是 1筑公,所以從索引位置 1 開始的字符串,就是 name 字段的值 "John"尊浪。第二個字段是 friendshipStatus匣屡,其索引值是 6,找到值為 2拇涤, 表示 NotFriend捣作。第三個字段是 spouse,也一個 Person 對象鹅士,索引值是 12券躁,指向的是此對象的支點位置。第四個字段是一個數(shù)組如绸,圖中表示的數(shù)組為空,所以索引值是 0旭贬。通過上面的解析怔接,可以看出,F(xiàn)latBuffers 通過自己分配和管理對象的存儲稀轨,使對象在內存中就是線性結構化的扼脐,直接可以把內存內容保存或者發(fā)送出去,加載“解析”數(shù)據(jù)只需要把 byte 數(shù)組加載到內存中即可奋刽,不需要任何解析瓦侮,也不產(chǎn)生任何中間變量。
它與具體的機器或者運行環(huán)境無關佣谐,例如在 Java 中肚吏,對象內的內存不依賴 Java 虛擬機的堆內存分配策略實現(xiàn),所以也是跨平臺的狭魂。
4.FlatBuffers使用步驟:
(1).定義數(shù)據(jù)結構 Schema
namespace Character;
table Items{
items:[Basic];
itemId:long;
timestemp:long;
}
table Basic{
id:long;
name:string;
email:string;
code:long;
isVip:bool;
count:int;
headUrl:string (deprecated);
carList:[Car];
}
table Car{
id:long;
number:long;
describle:string;
}
root_type Items;
(2).把 Schema 編譯成 Java
flatc.exe -j -b Character.fbs
(3).把生成的文件復制到項目中
(4).編寫代碼
序列化
FlatBufferBuilder builder = new FlatBufferBuilder();
int car1 = Car.createCar(builder, 1000l, 8000l, builder.createString("1000的描述"));
int car2 = Car.createCar(builder, 1001l, 8001l, builder.createString("1001的描述"));
int car3 = Car.createCar(builder, 1002l, 8002l, builder.createString("1002的描述"));
int basic1 = Basic.createBasic(builder, 2000,
builder.createString("2000產(chǎn)品"), true,
Basic.createCarListVector(builder, new int[]{car1, car2, car3}));
int basic2 = Basic.createBasic(builder, 2001,
builder.createString("2001產(chǎn)品"), true,
Basic.createCarListVector(builder, new int[]{car2, car3}));
int itemListId = Items.createItemListVector(builder, new int[]{basic1, basic2});
Items.startItems(builder);
Items.addItemList(builder,itemListId);
Items.addTimestemp(builder,2017l);
int endItems = Items.endItems(builder);
Items.finishItemsBuffer(builder,endItems);
FileOutputStream out = null;
FileChannel inChannel = null;
try{
if(mSerializeFile.exists()){
mSerializeFile.delete();
}
out = new FileOutputStream(mSerializeFile);
inChannel = out.getChannel();
ByteBuffer buffer =
builder.dataBuffer();
while (buffer.hasRemaining()) {
inChannel.write(buffer);
}
Toast.makeText(this,"序列號成功",Toast.LENGTH_LONG).show();
}
反序列化
in = new FileInputStream(mSerializeFile);
outChannel = in.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int readSize = 0;
while((readSize= outChannel.read(buffer))!=-1){
Log.e(TAG,"===>"+readSize);
}
buffer.flip();
Items rootAsItems = Items.getRootAsItems(buffer);
Log.e(TAG , "timestem ==>"+rootAsItems.timestemp());
int itemsLength
= rootAsItems.itemListLength();
for (int i = 0; i < itemsLength; i++) {
Basic basic = rootAsItems.itemList(i);
Log.e(TAG,"---id:"+basic.id()+"---name:"+basic.name());
int carListLength = basic.carListLength();
for (int i1 = 0; i1 < carListLength; i1++) {
Car car = basic.carList(i1);
Log.e(TAG,"-------------id:"+car.id()+"---describle:"+car.describle());
}
}
Toast.makeText(this,"序列號成功",Toast.LENGTH_LONG).show();
}
5.FlatBuffers使用建議:
(1).項目中有大量數(shù)據(jù)傳輸和解析罚攀,使用 JSON 成為了性能瓶頸時使用FlatBuffers
(2).穩(wěn)定的數(shù)據(jù)結構定義,可以使用FlatBuffers