《Android群英傳》讀書筆記10.Android效能優化
1.佈局優化
1.1. Android UI渲染機制
在Android中,系統通過VSYNC訊號觸發對UI的渲染、重繪,其間隔時間是16ms。這個16ms其實就是1000ms中顯示60幀畫面的單位時間,即1000/60。如果不能在16ms內完成繪製,那麼就會造成丟幀現象。
檢測UI渲染時間的工具:開發者選項→GPU呈現模式分析→在螢幕上顯示為條形圖。
1.2. 避免Overdraw
過度繪製會浪費很多的CPU、GPU資源,檢測工具:開發者選項→除錯GPU過度繪製→顯示過度繪製區域,可以通過介面上的顏色來判斷Overdraw的次數
1.3. 優化佈局層級
在Android中,系統對View進行測量、佈局和繪製時,都是通過對View樹的遍歷來進行操作的。如果一個View樹的高度太高,就會嚴重影響測量、佈局和繪製的速度。
1.4. 避免巢狀過多無用佈局
1. 使用<include>
標籤重用Layout
<include layout="@layout/common_ui"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
2. 使用<ViewStub>
實現View的延遲載入
<ViewStub>
是一個非常輕量級的元件,它不僅不可視,而且大小為0
<ViewStub
android:id="@+id/not_often_use"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/not_often_use" />
mViewStub = (ViewStub) findViewById(R.id.not_often_use);
View inflateView = mViewStub.inflate ();
TextView textView = (TextView) inflateView.findViewById(R.id.tv);
textView.setText("Haha!");
一旦<ViewtStub>
被設定為可見或是被inflate了,<ViewStub>
就不存在了,取而代之的是被infalte的Layout,並將這個Layout的ID重新設定為<ViewStub>
中通過android:inflatedId
屬性所指定的ID
<ViewStub>
標籤只會在顯示時,才去渲染整個佈局,而View.GONE
,在初始化佈局樹的時候就已經新增在佈局樹上了
1.5. Hierarchy Viewer
工具位於\sdk\tools\hierarchyviewer.bat,選擇要除錯的程序,然後點選上面的”Load View Hierarchy”
當點選其中一個View的時候,可以顯示該View的繪製情況。不過,第一次點選的時候,各種顯示的時間都是NA,需要點選選單中的”Profile Node”按鈕重新進行計算,才能獲取繪製資訊
2. 記憶體優化
2.1. 什麼是記憶體
由於Android應用的沙箱機制,每個應用所分配的記憶體大小是有限度的,記憶體太低就會觸發LMK——Low Memory Killer機制。通常情況下記憶體是指手機的RAM,它包括以下幾個部分:
- 暫存器(Registers):速度最快的儲存場所,因為位於處理器內部,在程式中無法控制
- 棧(Stack):存放基本型別的資料和物件的引用,但物件本身不存放在棧中,而是存放在堆中
- 堆(Heap):堆記憶體用來存放由new建立的物件和陣列。在堆中分配的記憶體,由Java虛擬機器的自動垃圾回收器(GC)來管理
- 靜態儲存區域(Static Field):指在固定的位置存放應用程式執行時一直存在的資料,Java在記憶體中專門劃分了一個靜態儲存區域來管理一些特殊的資料變數如靜態的資料變數
- 常量池(Constant Pool):JVM虛擬機器必須為每個被裝載的型別維護一個常量池,常量池就是該型別所用到常量的一個有序集合,包括直接常量(基本型別,String)和對其他型別、欄位和方法的符號引用
當定義一個變數,Java虛擬機器就會在棧中為該變數分配記憶體空間,當該變數作用域結束後,這部分記憶體空間會馬上被用作新的空間進行分配。如果使用new的方式建立一個變數,那麼就會在堆中為這個物件分配記憶體空間,即使該物件的作用域結束,這部分記憶體也不會立即被回收,而是等待系統GC進行回收。堆的大小隨著手機的不斷髮展而不斷變大。在程式中,可以使用如下程式碼來獲得堆的大小
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
int heapSize = manager.getLargeMemoryClass();
2.2. 獲取Android系統記憶體資訊
1. Process Stats
開發者選項→程序統計資訊 或 adb shell dumpsys procstats
2. Meminfo
設定→應用→執行中 或 adb shell dumpsys meminfo
2.3. 記憶體回收
Java建立了垃圾收集器執行緒(Garbage Collection Thread)來自動進行資源的管理,這樣做的好處是大大降低了程式開發人員對記憶體管理的繁瑣工作。Java的GC是系統自動進行的,但何時進行卻是開發者無法控制的,即使呼叫System.gc()
方法,也只是建議系統進行GC,但系統不一定採納建議。JVM虛擬機器雖然能夠自動控制GC,但也難免會存在部分物件忘記回收的現象發生,這就是造成記憶體洩漏的原因
2.4. 記憶體優化例項
1. Bitmap優化
Bitmap是造成記憶體佔用過高甚至是OOM(Out of Memory)的最大威脅,下面給出一些使用Bitmap的小技巧:
- 使用適當解析度和大小的圖片
如果圖片解析度與資原始檔夾解析度不匹配或者圖片解析度太高,就會導致系統消耗更多的記憶體資源。同時,在適當的時候,應該顯示合適大小的圖片,例如在圖片列表介面可以使用圖片的縮圖thumbnails,而在顯示詳細圖片的時候再顯示原圖;或者在對影象要求不高的地方,儘量降低圖片的精度 及時回收記憶體
bitmap.recycle(); // deprecated
- 使用圖片快取
通過記憶體快取(LruCache)和硬碟快取(DiskLruCache)可以更好地使用Bitmap
2. 程式碼優化
- 對常量使用
static
修飾符 - 使用靜態方法,靜態方法會比普通方法提高15%左右的訪問速度
- 減少不必要的物件,使用基礎型別會比使用物件更加節省資源,同時更應該避免頻繁建立短作用域的變數
- ……
3. Lint工具
Android Lint工具是Android Studio中整合的一個Android程式碼提示工具,它可以給你的佈局、程式碼提供非常強大的幫助
4. 使用Android Studio的Memory Monitor工具
Memory Monitor工具是Android Studio自帶的一個記憶體監視工具,使用時需選擇所除錯的應用。從記憶體變換的走勢圖,可以判斷關於記憶體的使用狀態,例如當記憶體持續增高時,可能發生記憶體洩露,當記憶體突然減少時,可能發生了GC等
5. 使用TraceView工具優化App效能
5.1. 生成TraceView日誌的兩種方法
1. 通過程式碼生成精確範圍的TraceView日誌
通過呼叫Debug.startMethodTracing()
方法開啟監聽,通過呼叫Debug.stopMethodTracing()
方法結束監聽,可以使用這兩個方法來包圍要監聽的程式碼塊,例如在 onCreate()
方法裡呼叫startMethodTracing()
方法來開始監聽,在onDestroy()
方法裡使用stopMethodTracing()
方法來結束監聽。TraceView的日誌將會儲存到/sdcard/dmtrace.trace檔案下,因此需要在Manifest檔案中增加如下許可權
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
2. 通過Android Device Monitor生成TraceView日誌
開啟Android Studio的Android Device Monitor工具,選擇要除錯的程序,點選工具欄中的”start method profiling”按鈕,選擇監聽模式(整體監聽/抽樣監聽)
5.2. 開啟TraceView日誌
Android Device Monitor→File→Open File…
5.3. 分析TraceView日誌
1. 時間軸區域
顯示不同執行緒在不同的時間段內的執行情況
2. Profile區域
顯示選擇的色塊所代表的方法在該色塊所處的時間段內的效能分析
6. 使用MAT工具分析App記憶體狀態
MAT, Memory Analyzer Tool
6.1. 生成HPROF檔案
開啟Android Device Monitor工具,選擇要監聽的執行緒,並點選選單欄中的”Update Heap”按鈕。在Heap標籤中,點選”Cause GC”按鈕,就會顯示出當前的記憶體狀態
除了手動檢視Heap狀態,還可以點選選單欄的”Dump HPROF File”按鈕,系統會生成一個(package).hprof檔案,不過對這個檔案我們還不能直接使用MAT工具進行分析,還需要進行格式轉換:
D:\sdk\platform-tools>hprof-conv F:\Heap\com.imooc.heap.hprof heap.hprof
6.2. 分析HPROF檔案
開啟MAT工具,選擇”Open a Heap Dump”選項,等待檔案匯入後,顯示分析結果。
MAT的幾個功能:
- Histogram
- Dominator Tree
7. 使用Dumpsys命令分析系統狀態
使用Dumpsys命令可以列出Android系統相關的資訊和服務狀態。使用Dumpsys命令時,只需要輸入adb shell dumpsys + 引數
即可,例如使用如下命令來獲取Activity棧的詳細資訊
adb shell dumpsys activity
下面總結了一些常用的Dumpsys引數
activity | 顯示所有的Activity棧的資訊 |
meminfo | 記憶體資訊 |
battery | 電池資訊 |
package | 包資訊 |
wifi | 顯示Wi-Fi資訊 |
alarm | 顯示alarm資訊 |
procstats | 顯示記憶體狀態 |
配合Linux下的shell命令,如grep, find等,可以讓Dumpsys命令發揮非常強大的作用