Android性能優(yōu)化篇之數(shù)據(jù)傳輸效率優(yōu)化

image

引言

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í)行速度更快四濒,內存的使用效率也更高换况。


image1.png
下面介紹三個數(shù)據(jù)序列化的候選方案:
  • Protocal Buffers:強大,靈活盗蟆,但是對內存的消耗會比較大戈二,并不是移動終端上的最佳選擇。
  • Nano-Proto-Buffers:基于Protocal喳资,為移動終端做了特殊的優(yōu)化觉吭,代碼執(zhí)行效率更高,內存使用效率更佳仆邓。
  • FlatBuffers:這個開源庫最開始是由Google研發(fā)的鲜滩,專注于提供更優(yōu)秀的性能。
    上面這些方案在性能方面的數(shù)據(jù)對比如下圖所示:


    image2.png
image3.png

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ù)類性,易于使用巷燥,跨平臺赡盘,幾乎語言無關。

官方提供了一個性能對比表


image4.png

在做 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的一張圖如下:


image5.png

可見啃炸,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).把生成的文件復制到項目中
image6.png
(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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末党觅,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子斋泄,更是在濱河造成了極大的恐慌杯瞻,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件炫掐,死亡現(xiàn)場離奇詭異魁莉,居然都是意外死亡,警方通過查閱死者的電腦和手機募胃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門旗唁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人摔认,你說我怎么就攤上這事逆皮。” “怎么了参袱?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵电谣,是天一觀的道長。 經(jīng)常有香客問我抹蚀,道長剿牺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任环壤,我火速辦了婚禮晒来,結果婚禮上,老公的妹妹穿的比我還像新娘郑现。我一直安慰自己湃崩,他們只是感情好,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布接箫。 她就那樣靜靜地躺著攒读,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辛友。 梳的紋絲不亂的頭發(fā)上薄扁,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天,我揣著相機與錄音废累,去河邊找鬼邓梅。 笑死,一個胖子當著我的面吹牛邑滨,可吹牛的內容都是我干的日缨。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼掖看,長吁一口氣:“原來是場噩夢啊……” “哼殿遂!你這毒婦竟也來了诈铛?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤墨礁,失蹤者是張志新(化名)和其女友劉穎幢竹,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體恩静,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡焕毫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了驶乾。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片邑飒。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖级乐,靈堂內的尸體忽然破棺而出疙咸,到底是詐尸還是另有隱情,我是刑警寧澤风科,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布撒轮,位于F島的核電站,受9級特大地震影響贼穆,放射性物質發(fā)生泄漏题山。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一故痊、第九天 我趴在偏房一處隱蔽的房頂上張望顶瞳。 院中可真熱鬧,春花似錦愕秫、人聲如沸慨菱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽符喝。三九已至,卻和暖如春等恐,著一層夾襖步出監(jiān)牢的瞬間洲劣,已是汗流浹背备蚓。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工课蔬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人郊尝。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓二跋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親流昏。 傳聞我的和親對象是個殘疾皇子扎即,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內容

  • 從三月份找實習到現(xiàn)在吞获,面了一些公司,掛了不少谚鄙,但最終還是拿到小米各拷、百度、阿里闷营、京東烤黍、新浪、CVTE傻盟、樂視家的研發(fā)崗...
    時芥藍閱讀 42,186評論 11 349
  • Java性能問題一直困擾著廣大程序員速蕊,由于平臺復雜性,要定位問題娘赴,找出其根源確實很難规哲。隨著10多年Java平臺的改...
    程序員技術圈閱讀 4,710評論 0 65
  • 1.ios高性能編程 (1).內層 最小的內層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結構(3).初始化時...
    歐辰_OSR閱讀 29,321評論 8 265
  • 想來2015年的主題應該是成長與感恩了。 沒有想到在大年二十九诽表,老師來學校竟然是給我送壓歲錢的唉锌。拿著心里暖暖的,w...
    anan081646閱讀 461評論 0 3
  • 雷雁雄8月26日總結:今天中午帶兒子去逛街关顷,下午有朋友來家里玩糊秆,有朋自遠方來,不亦樂乎议双。開心愉快接待好朋友痘番。
    雷雁雄閱讀 141評論 0 0