Android的效能優化方法
佈局優化
佈局優化的思想:儘量減少佈局檔案的層級。佈局的層級少了,這就意味著Android的繪製工作量少了,那麼程式的效能自然提高了。
如何進行佈局優化呢?
-
刪除佈局中無用的控制元件和層級
-
有選擇地使用效能較低的ViewGroup,比如RelativeLayout。
- 如果佈局中既可以使用LinearLayout,也可以使用RelativeLayout,那麼就採用LinearLayout,這是因為RelativeLayout的功能比較複雜,他的佈局過程需要花費更多的CPU時間。
- FrameLayout和LinearLayout一樣都是簡單高效的ViewGroup,因此優先考慮使用它們。如果需要巢狀的話,建議採用RelativeLayout.
-
採用標籤、和ViewStub
- 標籤主要用於佈局重用
- 標籤一般和配合使用,它可以降低佈局層次
- ViewStub則提供了按需載入的功能,當需要時才會將ViewStub的佈局載入到記憶體,這提高了程式的初始化效率。
標籤
標籤只支援android:layout_開頭的屬性。不過id是特例。
<include android:id="@+id/new_title"
anddroid:layout_width="match_parent"
android:layout_height="match_parent"
layout="@layout/title" />
標籤一般和標籤一起使用從而減少佈局的層級。當包含的佈局與的佈局是同樣的viewGroup,比如LinearLayout。那麼顯然中的佈局的LinearLayout是多餘的,通過標籤就可以去掉多餘的那一層LinearLayout.
ViewStub
ViewStub繼承了View,它非常輕量級且寬/高都是0;因此本身不參與任何的佈局和繪製過程。ViewStub的意義在於:按需要載入所需的佈局檔案,在實際開發中,很多佈局檔案在正常情況下不會顯示,比如網路異常時的介面,這個時候就沒必要再整個介面初始化的時候將其載入進來。因此ViewStub提高了程式初始化的效能。
<ViewStub android:id="@+id/stub_import" android:inflatedId="@+id/panel_import" android:layout="@layout/layout_network_error" android:layout_width="match_parent" android:layout_height="wrap=content" androiyeayout_gravity="bootom"
其中stub_import是ViewSrub的id,而panel_import是layout_network_error這個佈局的根元素的id.那麼如何做到按需載入呢?有兩種方式:
findViewById(R.id.stub.import).setVisibility(View.VISIBLE);
或者
View importPanel = findViewById(R.id.stub_import).inflate();
當ViewStub通過setVisibility或者inflate方法載入後,ViewStub就會被它內部的佈局替換掉。
繪製優化
繪製優化是指:View的onDraw方法要避免執行大量的操作,主要體現在兩個方面:
- onDraw中不要建立新的區域性物件,這是因為onDraw方法可能會被頻繁呼叫,這樣就會在一瞬間產生大量的臨時物件,不僅佔用了過多的記憶體還會導致系統頻繁gc,降低了程式的執行效率。
- onDraw方法中不要做耗時的任務,也不能執行上千萬次的迴圈操作,儘管每次操作都很輕量級,但大量迴圈仍然十分搶佔CPU的時間片,這會造成View的繪製過程不流暢。按照Goole效能優化的標準,View的繪製幀率保證60fps是最佳的,這就要求每幀繪製時間不超過16ms(1000/60)這個時間。
記憶體洩漏優化
記憶體優化在開發過程中是一個需要重視的問題,也是開發人員最最容易犯的錯誤之一。記憶體洩漏的優化主要分為兩個方面:
- 開發過程中避免寫出有記憶體洩漏的程式碼
- 通過一些分析工具比如MAT來找出潛在的記憶體洩漏繼而解決
場景1:靜態變數導致的記憶體洩漏
下面的這種情形是一種最簡單的記憶體洩漏,下面的程式碼直接導致Acitvity無法正常銷燬,因為靜態變數mContext引用了它。
public class MainActivity extends Activity {
private static Context mContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
}
}
場景2:單例模式導致的記憶體洩漏
單例模式帶來的記憶體洩漏是我們最容易疏忽的。
public class TestManger {
private List<OnDataArrivedListener> mOnDataArrivedListeners = new ArrayList<>();
private static final class SingletonHolder {
public static final TestManager INSTANCE = new TestManager();
}
private TestManager {
}
public static TestManager getInstance() {
return SingertonHolder.INSTANCE;
}
public synchronized void registerListener(onDataArrivedListener listener) {
if(!mOnDataArrivedListeners.contains(listener)) {
mOnDataArrivedListeners.add(listener);
}
}
public synchronized void unregisterListener(OnDataArrivedListener listener) {
mOndataArrivedListeners.remove(listener);
}
public interface OnDataArrivedListener {
void onDataArrived(Objecet data);
}
}
接著再讓 Activity實現OnDataArrivedListener介面並向TestManager註冊監聽,如下所示。下面的程式碼由於缺少解註冊的操作所以會引起記憶體洩漏,洩漏的原因是:Activity的物件被單例模式的TestManager所持有,而單例模式的特點是其生命週期和Application保持一致,因此Activity物件無法被即時釋放。
protected void onCreate(Bundle savedInstanceState) {
·······
TestManager.getInstance.registerListener(this);
}
場景3:屬性動畫導致的記憶體洩漏
屬性動畫中有一類無限迴圈的動畫,如果在Activity中播放此類動畫而沒有在onDestroy中停止動畫,那麼動畫就會一直播放下去,儘管無法在介面上看到動畫效果,並且這時候Activity的View會被動畫持有,而View持有Activity,最終Activity無法釋放。
響應速度優化和ANR日誌分析
響應速度優化的核心思想是:避免在主執行緒中做耗時操作。如果有耗時操作應該放線上程中執行,即採用非同步的方式執行耗時操作。響應速度過慢主要體現在Activity的啟動速度上,如果主執行緒做太多事,會導致Activity啟動時出現黑屏,甚至ANR。**Android規定,Activity如果5秒之內無法響應螢幕觸控事件或者鍵盤輸入事件就會出現ANR,而BroadcastReceive如果10秒之內還未執行操作也會出現ANR。**實際開發中ANR是很難從程式碼中發現的。那麼在開發過程中遇到了ANR,怎麼解決呢?其實當出現ANR後,系統會在/data/anr目錄下建立一個traces.txt,通過分析這個檔案就能定位ANR的原因
ListView和Bitmap優化
- ListView優化
- 採用ViewHolder並避免在getView中執行耗時操作
- 根據列表的滑動狀態來控制任務的執行頻率,比如當列表快速滑動時顯然是不適合開啟大量的非同步任務的
- 可以嘗試開啟硬體加速使ListView的滑動更加流暢
- ListView 的優化策略完全適用於GirdView
- Bitmap的優化:主要通過Bitmap.Options來根據需要對圖片進行取樣,取樣的過程中主要運用到了Bitmap.Options的inSampleSize引數,具體參考筆記Bitmap的高效載入
執行緒優化
執行緒優化的思想為:採用執行緒池,避免程式中存在大量的Thread。執行緒池可以重用內部的執行緒,從而避免了執行緒的建立和銷燬所帶來的效能開銷,同時執行緒池還能有效的控制執行緒池的最大併發數,避免大量的執行緒因互相搶佔系統資源從而導致阻塞現象的發生。因此在實際開發中,要儘量採用執行緒池,而不是每次都要建立一個Thread物件
一些效能優化的建議
- 避免建立過多的物件
- 不要過多的使用列舉,列舉佔用的記憶體空間要比整型大
- 常量使用static final來修飾
- 使用一些Android特有的資料結構,比如SparseArray和Pair等,它們都具有更好的效能
- 適當使用軟引用和弱引用
- 儘量採用靜態內部類,這樣可以避免潛在的由於內部類而導致的記憶體洩漏