第15章 Android性能優化
為什么要性能優化?
Android設備作為一種移動設備,CPU和內存往往受到一定的限制。
- 過多的使用內存會導致內存溢出,即OOM(Out Of Memory)
- 過多的使用CPU會導致手機出現卡頓甚至出現無法響應的情況,即ANR(Application Not Responding)
一些有效的優化方法
- 布局優化
使用<include>、<merge>和<ViewStub>標簽 - 繪制優化
在OnDraw()中一不要創建新的對象,二不要做耗時任務 - 內存泄露優化
養成良好的編程意識比如注意靜態變量、單例模式和屬性動畫造成的內存泄漏 - 相應速度優化
避免ANR,比如不要再主線程里做超過5s的耗時任務,不要再onReceive里面做超過10s的任務,還要注意由于在主線程中等待長時間的同步鎖而導致的ANR - ListView優化
在Adapter中利用ViewHolder,在滑動時不要加載數據,開啟硬件加速 - Bitmap優化
利用BitmapFactory.Options對象對圖片進行壓縮 - 線程優化
采用線程池從而避免大量的創建銷毀線程,還能控制并發數避免阻塞現象 - 其他優化
- 避免創建過多的對象
- 不要過多使用枚舉
- 常量使用static final修飾
- 使用Android特有的數據結構比如SparseArray和Pair等
- 適當使用軟引用和弱引用
- 采用內存緩存LreCache和磁盤緩存DiskLruCache
- 盡量采用靜態內部類
15.1 Android的性能優化方法
本節介紹了一些有效的性能優化方法。
15.1.1 布局優化
主要思想:布局中的層級少了,這就意味著Android的繪制工作少了,那么程序的性能自然就提高了。
主要方法:使用<include>、<merge>和<ViewStub>標簽
<include>標簽
<include>標簽可以將指定布局加載到當前的布局文件中,通過layout屬性來設置指定的布局文件,<include>標簽只支持帶有android:layout_*這種屬性,而且只要設定了相關屬性,就必須存在android:layout_width和android:layout_height這兩個屬性。使用方法如下所示
<include layout="@layout/titlebar"/>
<merge>標簽
<merge>標簽主要要和<include>標簽搭配使用,當<include>標簽的最外層布局和所處的當前布局一樣時,比如說都是垂直方向的LinearLayout,此時便可以將<include>標簽的最外層布局的LinearLayout改成<merge>標簽,如下所示
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Title"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Content"/>
</merge>
<ViewStub>標簽
<ViewStub>標簽主要是一個輕量級的按需加載的布局控件,特點是像<include>標簽一樣包含一個布局文件,還有一個是可以在必要的時候顯示和消失,所以ViewStub控件有利于顯示一些網絡加載或者異常時的界面。使用方法如下。
在布局文件中:
<ViewStub
android:id="@+id/stub"
android:inflatedId="@+id/titlebar_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/titlebar"
/>
在代碼中:需要注意的點就是上面inflatedId的理解,它的意思就是包含的根布局id,在上面的代碼中就是titlebar.xml這個布局的id,然后通過該id可以獲取到titlebar里的控件,比如下面的tvTitle
view = stub.inflate();
View titleBar = findViewById(R.id.titlebar_id);
tvTitle = (TextView) titleBar.findViewById(R.id.title);
btnSwitch.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(view.getVisibility() == View.VISIBLE)
view.setVisibility(View.GONE);
else
view.setVisibility(View.VISIBLE);
}
});
btnChange.setOnClickListener(new OnClickListener() {
int i = 0;
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
tvTitle.setText("i = " + i++);
}
});
15.1.2 繪制優化
繪制優化是指View的onDraw方法要避免大量的操作,主要體現在內存和CPU兩個方面:
- onDraw中不要創建新的局部對象
因為onDraw可能會被頻繁的調用而導致大量的對象被創建,這會導致占用過多的內存從而頻繁地gc; - onDraw中不要進行耗時的操作
頻繁的耗時操作會占用CPU的時間大量的時間,導致程序卡頓
15.1.3 內存泄漏優化
內存泄漏:程序在銷毀時,還有一些引用沒有被釋放,導致這些對象占用的內存無法被使用
解決方法:
在開發過程避免寫出有內存泄漏的代碼,以下是幾種容易造成內存泄漏的原因
- 靜態變量導致的內存泄漏
- 單例模式導致的內存泄漏
需要在不需要時解除 - 屬性動畫導致的內存泄漏
所以使用屬性動畫一定要記得在不可見時取消動畫
15.1.4 響應速度優化和ANR分析
響應速度優化的核心思想是避免在主線程做耗時的操作,將耗時的操作放在線程里執行。同時還要注意另一種不是很明顯的造成ANR的原因:在主線程中長時間等待同步鎖
15.1.5 ListView和Bitmap優化
ListView優化
ListView優化主要發生在Adapter的getView中,14章里有分析,這里總結一下
- 采用ViewHolder進行緩存并且避免在getView中執行耗時操作
- 在滑動時不要加載數據
- 嘗試開啟硬件加速
Bitmap優化
Bitmap優化主要是通過BitmapFactory的Options對象進行的
方法:利用Options先計算出采樣率inSampleSize并返回給Options,最后通過改變的Options對圖片進行壓縮
15.1.6 線程優化
線程優化主要體現在避免大量的創建和銷毀線程,因此線程優化的思想就是使用線程池。使用線程池的好處就是,可以重用線程避免創建和銷毀大量線程,還可以控制并發數以避免阻塞
15.1.7 一些性能優化建議
- 避免創建過多的對象
- 不要過多使用枚舉
- 常量使用static final修飾
- 使用Android特有的數據結構比如SparseArray和Pair等
- 適當使用軟引用和弱引用
- 采用內存緩存LreCache和磁盤緩存DiskLruCache
- 盡量采用靜態內部類
15.2 內存泄漏分析之MAT工具
15.3 提高程序的可維護性
本節主要是講Android的程序設計思想,主旨是如何提高代碼的可維護性和可擴展性
代碼風格
- 命名要規范
私有成員以m開頭,靜態成員以s開頭,常量則全部用大寫字母表示等等 - 代碼的排版上需要留出合理的空白來區分不同的代碼塊
- 僅為非常關鍵的代碼添加注釋,其他地方不注釋
層次性
代碼的層次性是指代碼要有分層的概念,不要試圖在一個方法或者一個類中去全部實現,而要將它分成幾個自邏輯,然后每個子邏輯做自己的事情
擴展性
程序的擴展性要在寫程序的過程中時刻考慮到,考慮著如果這個邏輯后面發生了改變那么需要做哪些修改,以及怎么樣才能降低修改的工作量
設計模式
恰當地使用設計模式可以提高代碼的可維護性和可擴展性,但是Android程序有性能瓶頸,因此要控制設計的度,設計不能太牽強