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