Android效能優化—儲存優化

2020-10-26 12:01:36

Android 儲存方式

SharedPrefence,儲存簡單的設定資料等
SQLite,儲存複雜的關係型資料
File,一般儲存紀錄檔檔案,本地檔案快取, protobuf ,7z
ContentProvider,跨程序資料存取,一般和SQLite結合使用,把資料提供給其他app程序使用。
網路儲存 , 網路儲存 涉及到序列化/反序列化(protobuf,xml,json)

SharedPrefence

SharedPrefence的commit和apply

  • apply沒有返回值,而commit返回boolean表明修改是否提交成功 ;
  • commit是把內容同步提交到硬碟的,而apply先立即把修改提交到記憶體,然後開啟一個非同步的執行緒提交到硬碟,並且如果提交失敗,你不會收到任何通知。
  • 所有commit提交是同步過程,效率會比apply非同步提交的速度慢,在不關心提交結果是否成功的情況下,優先考慮apply方法。
  • apply是使用非同步執行緒寫入磁碟,commit是同步寫入磁碟。所以我們在主執行緒使用的commit的時候,需要考慮是否會出現ANR問題。(不適合大量資料儲存)

多程序問題 -> mmkv

SQLite

SQLiteStatement
使用事務
使用索引
非同步執行緒,寫資料庫統一管理

網路

  • 序列化
  • 反序列化

xml
json
protobuf 與平臺無關
7z壓縮 (作業)

protobuf

已經有xml/json,為什麼要用protobuf

相對於xml,json,protobuf的優點:

  • 簡潔:xml要寫一大堆解析的程式碼,而protobuf自動生成程式碼
  • 體積小:訊息大小隻有xml的1/10 - 1/3
  • 速度快:解析速度比xml快20~100倍
  • 使用Protobuf編譯系統: 可以生成更容易在程式設計中使用的資料存取程式碼
  • 更好的相容性,Protocol Buffers設計的一個原則就是要能夠很好的支援向下或向上相容。

缺點:
1、二進位制格式導致可讀性差
為了提高效能,protobuf採用了二進位制格式進行編碼。這直接導致了可讀性差。這個直接影響開發測試時候的效率。當然,一般情況下,protobuf非常可靠,並不會出現太大的問題。
2、缺乏自描述
一般來說,XML是自描述的,而protobuf格式則不是。 給你一段二進位制格式的協定內容,不配合你寫的結構體是看不出來什麼作用的。

專案中使用protobuf

在命令列中使用protobuf
如果要用命令將protobuf檔案(protobuf檔案的字尾為.proto)編譯為java檔案,需要安裝protobuf工具,然後使用protoc 命令進行編譯。

在Android studio專案中使用protobuf
1)首先,我們需要在全域性的buid.gradle檔案中新增上protobuf工具外掛:

classpath ‘com.google.protobuf:protobuf-gradle-plugin:0.8.10’

2)然後,要app/buid.gradle檔案中:

①新增上此外掛

    apply plugin: 'com.google.protobuf'

②新增依賴

     implementation 'com.google.protobuf:protobuf-java:3.7.1'

③新增protobuf 設定

protobuf {
    //設定protoc編譯器
    protoc {
        artifact = 'com.google.protobuf:protoc:3.7.1'
    }
    //這裡設定生成目錄,編譯後會在build的目錄下生成對應的java檔案
   generateProtoTasks {
        all().each { task ->
           task.builtins {
                remove java
           }
           task.builtins {
                java {}
           }
        }
    }
}

④新增 protobuf檔案的目錄(在android {}裡面)

sourceSets {
      main {
          proto {
              srcDir 'src/main/java/com/protobufdir'
          }
      }
}

3)在src/main/java/com/protobufdir目錄下新建protobufbean.proto檔案,新增如下程式碼:

syntax = "proto3";
package com.example.protobufdemo;//包名


option java_package = "com.example.protobuf02";
option  java_outer_classname = "Person";
option csharp_namespace = "android";
message _Person{

    string name = 1;
    int32 id = 2;
    string email = 3;

    enum _PhoneType{
        MOBILE = 0;
        HOME = 1;
        WORK = 2;
    }

    message _PhoneNumber{
        string number =1;
        _PhoneType type = 2;
    }

    repeated _PhoneNumber phone = 4;//陣列


}

4) 重新build專案,AS build專案時,會將.proto檔案生成java檔案。
可以在\app\build\generated\source\proto\debug\java\ 目錄下找到這些生成的java檔案,
這些生成的java檔案封裝好了一些序列化和反序列化的API ,這些API 可以直接在專案中呼叫 。

語言規範

訊息定義

  • 確定訊息命名,給訊息取一個有意義的名字。
  • 指定欄位的型別
  • 定義欄位的編號,在Protocol Buffers中,欄位的編號非常重要,欄位名僅僅是作為參考和生成程式碼用。需要注意的是欄位的編號區間範圍,其中19000 ~19999被Protocol Buffers作為保留欄位。

欄位約束

  • required指定該欄位必須賦值,禁止為空(在v3中該約束被移除);
  • optional指定欄位為可選欄位,可以為空,對於optional欄位還可以使用[default]指定預設值,如果沒有指定,則會使用欄位型別的預設值;
  • 使用repeated指定欄位為集合

欄位型別

  • 在一個proto檔案中可以同時定義多個message型別,生成程式碼時根據生成程式碼的目標語言不同,處理的方式不太一樣,如Java會針對每個message型別生成一個.java檔案
  • 可以指定欄位的型別為其他message型別
  • 使用import關鍵字彙入其他proto檔案
  • 在proto檔案中訊息的型別還可以巢狀
  • 在proto檔案中可以使用extensions關鍵字預留一部分欄位編號出來,以便於後期給第三方擴充套件時使用
  • oneof關鍵字指定一組欄位中,至少要有一個欄位必須賦值

在Protocol Buffers中提供了很多的標量型別,供我們在定義欄位型別時使用:
在這裡插入圖片描述

編碼協定

Base-128變長編碼

  • 所謂變長編碼是和定長編碼相對的,定長編碼使用固定位元組數來表示,如int32型別的數位固定使用4 bytes表示,而變長編碼是需要幾個位元組就使用幾個位元組,如對於int32型別的數位1來說,只需要1 bytes足夠。Base-128變長編碼的原則就兩條
  • 每個位元組使用使用低7位表示數位,除了最後一個位元組,其他位元組的最高位都設定為1。
  • 採用Little-Endian位元組序
  • tag-length-valuetag-length-value

在這裡插入圖片描述

負數編碼處理

  • 採用ZigZag Encoding
  • (n <<1)^(n>>31)或者(n<<1)^(n>>63)

大端序列
先寫高位,再寫低位
小端序列
先寫低位,再寫高位

Protocol Buffers 序列化協定及應用
【Protobuf】Protobuf的編解碼規則詳解