Android作為一種移動設備,內(nèi)存和CPU資源都是受限的. 過多地使用內(nèi)存會導致內(nèi)存溢出(OOM),過多的使用CPU會導致手機卡頓,甚至出現(xiàn)程序無法響應的情況(ANR). 本文介紹一系列優(yōu)化方法.
1. 布局優(yōu)化
1.1 盡量減少布局的層級.
布局的層級少了,那么Android繪制時的工作量就少了,自然能后提高性能.
1.2 ViewGroup的選擇.
有選擇的使用性能較低的ViewGroup如RelativeLayout的功能比較復雜繪制過程消耗更多的CPU資源 , 盡量使用性能較高的ViewGroup如LinearLayout 和FrameLayout.
1.3 <include> 標簽
<include> 標簽可以將指定的布局加載到當前布局 .
定義一個布局文件,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>
使用 <include>標簽引用布局.
<?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 <merge> 標簽
<merge> 標簽主要和 <include> 標簽一起使用,比如在上面的樣例中當前布局使用了一個豎直方向的LinearLayout,在被引用的布局中也使用了一個豎直方向的LinearLayout此時內(nèi)層的LinearLayout就顯得多余了.可以使用<merge>標簽替代.從而達到減少布局層級的目的.
<?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,它本身不參與任何布局和繪制過程. 他可以按需添加布局,有些布局一開始是不需要的,因此可以先不加載,在需要時再進行布局加載.
創(chuàng)建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節(jié)點
<?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不支持<merge>標簽
2. 繪制優(yōu)化
繪制優(yōu)化主要是指View的onDraw方法要避免大量操作. 主要體現(xiàn)如下兩方面 :
- onDraw中不要創(chuàng)建新的局部對象, 這是應為onDraw方法會被頻繁調(diào)用,這樣會在一瞬間產(chǎn)生大量臨時對象.這樣會造成過多的內(nèi)存效果,更多的GC,降低程序的執(zhí)行效率.
- onDraw方法中不要進行耗時操作,也不能執(zhí)行大量的循環(huán)操作.雖然每次都是輕量級的但是大量循環(huán)還是會十分搶占CPU的時間片.造成View的繪制不流暢.按照Google給出的性能優(yōu)化典范中的標準,View的幀率保證60fps是最好的.這就要求每一幀圖像的繪制不要超過 1000 / 60 = 16ms .因此盡量減低onDraw的復雜度有利于提高性能.
3. 內(nèi)存泄露優(yōu)化
內(nèi)存泄露的優(yōu)化分為兩部分.
- 開發(fā)過程中避免寫內(nèi)存泄露的代碼.
- 通過一些工具如MAT找出潛在的內(nèi)存泄露,然后解決掉.
3.1 靜態(tài)變量導致內(nèi)存泄露
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);
}
}
上面的代碼容易造成內(nèi)存泄露.
3.2 單例模式導致的內(nèi)存泄露
單例模式造成內(nèi)存泄露主要是單例對象保存的外部變量的引用沒有被移除造成的.比如單例對象有添加監(jiān)聽者的方法但是沒有移除監(jiān)聽者對象的方法.
3.3 屬性動畫導致的內(nèi)存泄露
Android 3.0 增加了屬性動畫,這種動畫需要在onDestory中取消動畫animator.cancel()
否則動畫就會無限循環(huán)地執(zhí)行,造成動畫對象會持有View,View持有Activity,最終造成Activity無法釋放.造成內(nèi)存泄露.
3.4 響應速度優(yōu)化和ANR日志分析
Android系統(tǒng)規(guī)定:
- Activity如果 5 秒內(nèi)無法響應屏幕觸摸或者鍵盤輸入時間就會出現(xiàn)ANR.
- BroadcastReciever 10秒內(nèi)沒有執(zhí)行完操作,就會出現(xiàn)ANR.
當系統(tǒng)發(fā)生ANR時會在 data/anr
目錄下生成traces.txt
文件.通過分析這個文件就可以找到ANR的原因.
我們在onCreate方法中添加如下代碼模模擬ANR
while(true);
查看traces.txt部分內(nèi)容如下:
便可以定位出問題所在了.
4. ListView 和 Bitmap優(yōu)化.
4.1 ListView 優(yōu)化主要有三個方面
- 使用ViewHolder,避免在getView中進行耗時操作.
- 根據(jù)列表的滑動狀態(tài)來控制任務的執(zhí)行頻率,比如當列表快速滑動時不適合開啟大量異步任務.
- 開啟硬件加速來使ListView更加流暢.
4.2 Bitmap優(yōu)化
Bitmap優(yōu)化主要是通過加載壓縮后的圖片來進行優(yōu)化的.
5. 線程優(yōu)化
盡量使用線程池代替開啟新的線程.
6. 性能優(yōu)化建議
- 避免創(chuàng)建過多的對象.
- 不要過多的使用枚舉,枚舉占用的空間比整形大.
- 常量使用 static final 來修飾.
- 使用一些Android 特有的數(shù)據(jù)結構,SparseArray 和 Pair 他們具有更好的性能.
- 適當使用軟引用和弱引用.
- 采用內(nèi)存緩存和磁盤緩存.
- 盡量使用靜態(tài)內(nèi)部類,避免潛在的由于內(nèi)部類導致的內(nèi)存泄露.
參考 : <Android開發(fā)藝術探索>