Android作爲一種移動設備,內存和CPU資源都是受限的. 過多地使用內存會導致內存溢出(OOM),過多的使用CPU會導致手機卡頓,甚至出現程序無法響應的情況(ANR). 本文介紹一系列優化方法.
1. 佈局優化
1.1 儘量減少佈局的層級.
佈局的層級少了,那麼Android繪製時的工作量就少了,自然能後提高性能.
1.2 ViewGroup的選擇.
有選擇的使用性能較低的ViewGroup如RelativeLayout的功能比較複雜繪製過程消耗更多的CPU資源 , 儘量使用性能較高的ViewGroup如LinearLayout 和FrameLayout.
1.3 標籤
標籤可以將指定的佈局加載到當前佈局 .
定義一個佈局文件,layout_include.xml
文件.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#f00"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:text="測試按鈕"
android:textSize="30sp"
android:layout_height="wrap_content"/>
</LinearLayout>
使用 標籤引用佈局.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:id="@+id/activity_main"
android:background="#0f0"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 使用 include 標籤 -->
<include
android:layout_height="200dp"
android:layout_width="200dp"
layout="@layout/layout_include"/>
</LinearLayout>
效果圖如下:
1.4 標籤
標籤主要和 標籤一起使用,比如在上面的樣例中當前佈局使用了一個豎直方向的LinearLayout,在被引用的佈局中也使用了一個豎直方向的LinearLayout此時內層的LinearLayout就顯得多餘了.可以使用標籤替代.從而達到減少佈局層級的目的.
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<Button
android:layout_width="wrap_content"
android:text="測試按鈕"
android:textSize="30sp"
android:layout_height="wrap_content"/>
</merge>
1.5 ViewStub
ViewStub繼承自View並且寬高都是 0,它本身不參與任何佈局和繪製過程. 他可以按需添加布局,有些佈局一開始是不需要的,因此可以先不加載,在需要時再進行佈局加載.
創建layout_viewstub.xml
文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="@+id/stub_import"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:background="#00f"
android:layout_width="match_parent"
android:text="View_Stub測試界面"
android:textSize="30sp"
android:layout_height="match_parent"/>
</LinearLayout>
在activity_main.xml
中添加ViewStub節點
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:id="@+id/activity_main"
android:background="#0f0"
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 使用 include 標籤 -->
<include
android:layout_height="200dp"
android:layout_width="200dp"
layout="@layout/layout_include"/>
<!-- 使用ViewStub -->
<ViewStub
android:layout_width="200dp"
android:id="@+id/panel_import"
android:layout="@layout/layout_viewstub"
android:inflatedId="@+id/stub_import"
android:layout_height="200dp"/>
<Button
android:layout_width="wrap_content"
android:onClick="onclick"
android:text="加載佈局"
android:layout_height="wrap_content"/>
</LinearLayout>
修改MainActivity.java
public void onclick(View view) {
// 加載ViewStub方式一
// ((ViewStub)findViewById(R.id.panel_import)).setVisibility(View.VISIBLE);
// 加載ViewStub方式二
((ViewStub)findViewById(R.id.panel_import)).inflate();
}
android:id="@+id/panel_import"
: 設置ViewStub的id.
android:id="@+id/panel_import"
: ViewStub加載的佈局的根佈局的id.
android:layout="@layout/layout_viewstub"
: 設置加載佈局的資源id.
注意 : ViewStub不支持標籤
2. 繪製優化
繪製優化主要是指View的onDraw方法要避免大量操作. 主要體現如下兩方面 :
- onDraw中不要創建新的局部對象, 這是應爲onDraw方法會被頻繁調用,這樣會在一瞬間產生大量臨時對象.這樣會造成過多的內存效果,更多的GC,降低程序的執行效率.
- onDraw方法中不要進行耗時操作,也不能執行大量的循環操作.雖然每次都是輕量級的但是大量循環還是會十分搶佔CPU的時間片.造成View的繪製不流暢.按照Google給出的性能優化典範中的標準,View的幀率保證60fps是最好的.這就要求每一幀圖像的繪製不要超過 1000 / 60 = 16ms .因此儘量減低onDraw的複雜度有利於提高性能.
3. 內存泄露優化
內存泄露的優化分爲兩部分.
- 開發過程中避免寫內存泄露的代碼.
- 通過一些工具如MAT找出潛在的內存泄露,然後解決掉.
3.1 靜態變量導致內存泄露
public class MainActivity extends AppCompatActivity {
private static Context sContext;
private static View sView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 設置
sContext = this;
sView = new View(this);
}
}
上面的代碼容易造成內存泄露.
3.2 單例模式導致的內存泄露
單例模式造成內存泄露主要是單例對象保存的外部變量的引用沒有被移除造成的.比如單例對象有添加監聽者的方法但是沒有移除監聽者對象的方法.
3.3 屬性動畫導致的內存泄露
Android 3.0 增加了屬性動畫,這種動畫需要在onDestory中取消動畫animator.cancel()
否則動畫就會無限循環地執行,造成動畫對象會持有View,View持有Activity,最終造成Activity無法釋放.造成內存泄露.
3.4 響應速度優化和ANR日誌分析
Android系統規定:
- Activity如果 5 秒內無法響應屏幕觸摸或者鍵盤輸入時間就會出現ANR.
- BroadcastReciever 10秒內沒有執行完操作,就會出現ANR.
當系統發生ANR時會在 data/anr
目錄下生成traces.txt
文件.通過分析這個文件就可以找到ANR的原因.
我們在onCreate方法中添加如下代碼模模擬ANR
while(true);
查看traces.txt部分內容如下:
便可以定位出問題所在了.
4. ListView 和 Bitmap優化.
4.1 ListView 優化主要有三個方面
- 使用ViewHolder,避免在getView中進行耗時操作.
- 根據列表的滑動狀態來控制任務的執行頻率,比如當列表快速滑動時不適合開啓大量異步任務.
- 開啓硬件加速來使ListView更加流暢.
4.2 Bitmap優化
Bitmap優化主要是通過加載壓縮後的圖片來進行優化的.
5. 線程優化
儘量使用線程池代替開啓新的線程.
6. 性能優化建議
- 避免創建過多的對象.
- 不要過多的使用枚舉,枚舉佔用的空間比整形大.
- 常量使用 static final 來修飾.
- 使用一些Android 特有的數據結構,SparseArray 和 Pair 他們具有更好的性能.
- 適當使用軟引用和弱引用.
- 採用內存緩存和磁盤緩存.
- 儘量使用靜態內部類,避免潛在的由於內部類導致的內存泄露.
參考 :