1. 程式人生 > >Android 效能優化之常用優化點

Android 效能優化之常用優化點

資源類效能分為:磁碟、CPU和記憶體,以及與環境密切相關的網路和因為行動網路而顯得很重要的電池(耗電)。

1、磁碟

1.1 發現定位工具:Strict Mode 和 Systrace。
對於Strict Mode 的原理,主要是在檔案操作(BlockGuardOs.java)、資料庫操作(SQLiteConnection.java)和 SharePreferences操作(SharePreferencesImpl.java)的介面中插入檢查的程式碼。

1.2 具體優化點

  • 使用快取,避免重複多次讀寫某個檔案。(比如多次重複讀寫 /proc/cpuinfo)
    每次開啟、關閉或者讀/寫檔案,作業系統都需要從使用者態到核心態的切換,這種切換本身是很耗時的。
  • 延遲寫入,對於 SharePreferences 的 commit() ,一個方法中只需保留最後一個。
    每呼叫一次 commit(),都會對應一次檔案的開啟和關閉。
  • 在子執行緒中讀寫檔案
  • 對於讀寫檔案,使用快取輸入輸出流
    比如對於 ObjectOutputStream 在序列化磁碟時,在儲存物件的時候,每個資料成員會帶來一次 I/O 操作,導致 I/O 次數巨多。這時可以使用 BufferedOutputStream 來包裝一下。
  • 合理設定 Buffer 的大小
    Buffer 設定太小會導致讀寫次數太多,Buffer至少應為 4k,Java 預設是 8k。
  • 檔案解/壓縮效率
    解壓磁碟上的檔案,建議使用 ZipFile,效率較 ZipInputStream 提升 15% - 27%。
    如果檔案不在磁碟上(如網路)或者順序解壓一小部分檔案,或者zip檔案目錄遭到損壞,使用 ZipInputStream。
    ZIP壓縮大量小檔案時使用 ZipInputStream。
  • 避免重複多次開啟資料庫,使用快取資料庫連線。(開啟資料庫比較耗時)
  • 減少使用 AUTOINCREMENT,使用 AUTOINCREMENT 會增加 INSERT 耗時1倍以上。
  • 對於 Bitmap 解碼,使用 decodeStream 代替 decodeFile;使用 decodeResourceStream 代替 decodeResource。可以提高效率。

2、記憶體

2.1 原理

  • 虛擬機器的堆記憶體最大值
    在開發中,要注意App佔用的記憶體大小。Android系統給堆(Heap)設定了一個最大值,可通過 runtime.getruntime().maxmemory() 獲取。
  • Low Memory Killer 機制
    當手機剩餘記憶體低於記憶體的警戒線閾值時,就會觸發 LMK 機制殺掉 APP。
  • GC
    沒有被GC Root間接或直接引用的物件的記憶體機會被回收。
    需要注意 GC for Alloc,這種情況是在記憶體不足以分配給新的物件時觸發。它stop the world 的時間因為GC無法併發而變得更長。
    還要注意記憶體抖動問題。
  • 注意 Activity 洩漏
    Activity 物件會間接或直接引用 View、Bitmap等,所以一旦無法釋放,會佔用大量記憶體。
  • 圖片快取
    官方建議使用 Lru 演算法來做圖片快取。
    快取方案: LruCache,DiskLruCache 和 BlobCache。

    記憶體問題主要包括:
    1、常駐問題(主要是圖片快取)
    2、洩漏問題
    3、OOM
    4、GC問題(GC for alloc,記憶體抖動)

    後果可能或導致 App Crash、閃退、後臺被殺、卡頓,嚴重就導致 OOM。

2.2 工具

工具 問題 能力
top / procrank 記憶體佔用過大,洩漏 發現
memory info 洩漏 發現 + 初步定為
Strick Mode Activity 洩漏 自動發現 + 定位
Leak Canary Activity 洩漏 自動發現 + 定位
Systrace GC導致的卡頓 發現
Allocation Tracker 申請記憶體過大;輔助GC log發現的問題;記憶體抖動 發現 + 定位
MAT 記憶體洩漏 發現 + 定位

GC log
在 logcat 中可以看到 gc 日誌,對於log中 gc 的原因主要有如下幾種:

  • GC_EXPLICIT:通過 Runtime.gs() 與 VMRuntime.gc(),SIGUSR1 觸發產生的 GC。
  • GC_FOR_ALLOC:沒有足夠記憶體空間給予即將分配的記憶體,這時會觸發。這種GC不是併發GC,對卡頓影響更大。
  • GC_FOR_CONCUREENT:當超過堆佔用閾值時自動觸發,常見、健康GC。
  • GC_BEFORE_OOM:觸發 OOM 之前觸發 GC。不能併發,不能區域性GC,所以耗時長,容易卡頓。
  • GC_HPROF_DUMP_HEAP:在 dump 記憶體之前觸發的 GC。不能併發,不能區域性GC,所以耗時長,容易卡頓。

2.3 具體優化點

  • 非靜態內部類造成記憶體洩漏。(內部類持有外部類的引用)
  • Activity 物件被其他物件直接引用導致回收不掉,造成洩漏。
  • Activity 的 Context 被引用造成洩漏。
  • Thread 和 Runnabl 沒有執行或沒有執行完造成洩漏。
  • 定時器 Timer 沒有取消導致的記憶體洩漏。
  • Handler 和 呼叫view.postDelayed() 方法後沒有移除掉沒有執行的Message造成的洩漏。

3、CPU

3.1 工具

  • adb shell top
  • adb shell dumpsys cpuinfo
  • Systrace
  • Traceview

3.2 具體優化點

  • 當App退到後臺時,應該停止掉正在執行的不需要的任務。
  • 優化演算法。
  • 使用 renderscript 來減少影象處理的 CPU 消耗。

4、電池

4.1 原理
Android 官方建議廠商通過 PowerMonitor 之類的工具來測試每個硬體模組的耗電情況,並配置好 power_profile.xml 檔案。power_profile.xml 是一個為了讓Android系統能通過硬體呼叫頻率和強度來計算耗電的配置。

從檔案的內容可以看出耗電的硬體有:CPU、Wi-Fi、Radio(資料網路)、Senor(感應器)、BlueTooth(藍芽)、Screen(螢幕)、GPS。還有不屬於硬體模組的視訊音訊

adb檢視命令:adb shell dumps power

4.2 具體優化點

  • 對於有些功能,新增黑屏判斷。比如有些功能在黑屏狀態下是沒有意義的情況。
  • 黑屏狀態或者在後臺時,及時釋放動畫。
    注意使用 SurfaceView 自定義的動畫,SurfaceView 是新開了一個執行緒用於繪製。
  • 多個 AlarmManager 定時相近任務進行合併。
  • 注意 WakeLock 的及時釋放。
    注意間接呼叫:Mediaserver 是系統服務,是媒體功能的核心,音視訊的播放、照相、攝像、錄音等功能都需要呼叫到Mediaserver 實現各種媒體相關的功能。當App呼叫Mediaserver的某些功能時,例如音視訊錄製,Mediaserver 會申請WakeLock。比如 AudioRecord 會申請一個WakeLock 來避免手機進入休眠狀態影響音訊的錄製,當不需要時,應呼叫 release() 釋放相應資源。
  • 注意 WakeLock 的計數機制。(不同廠商可能不一樣,有修改)
    WakeLock 的 setReferenceCounted 介面用來設定計數機制,true為計數,false為不計數。計數即是每一個 acquire 必須對應一個 release;不計數則是無論有多少個 acquire,一個 release 就可以釋放。

參考:騰訊SNG專項測試團隊《Android 移動效能實戰》