一、簡介
眾所周知的Android系統(tǒng)每隔16ms重新繪制一次視圖,也就是說你的app必須在16ms內(nèi)完成屏幕刷新的所有邏輯操作,這樣才能達(dá)到60幀/s,這個幀率對人的肉眼來說是流暢的。如果達(dá)不到,用戶會感到卡頓。
另外,Android系統(tǒng)通過CPU和GPU共同完成視圖的渲染,大概流程如下:
1、Cpu通過measure、layout、record、execute幾個過程來對視圖進(jìn)行處理,得到一些多邊形和紋理。
2、Cpu通過指令(API:openGL ES)把這些多邊形、紋理發(fā)給gpu。
3、Gpu進(jìn)行格柵化,格柵化就是將例如字符串、按鈕、路徑或者形狀的一些高級對象,拆分到不同的像素上在屏幕上進(jìn)行顯示。
二、優(yōu)化方向

CPU的優(yōu)化:從減輕加工View對象成Polygons和Texture來下手View Hierarchy中包涵了太多的沒有用的view,這些view根本就不會顯示在屏幕上面,一旦觸發(fā)測量和布局操作,就會拖累應(yīng)用的性能表現(xiàn)。所以盡量減少不必要的View以及減少層級嵌套。
GPU優(yōu)化:從減輕柵格化來入手,避免同一個像素點(diǎn)被重復(fù)繪制多次,也就是避免過度繪制(overdraw)
那么根據(jù)上述的兩個方向,最終落地為兩點(diǎn):層級的優(yōu)化 、避免過度繪制。 下面分別來分析。
三、檢查工具:
Lint :
Preferences /Editor /Inspections 下查看Lint的配置規(guī)則:

如果勾線了這兩項的話,那么你運(yùn)行 Analyze/Inspect Code的時候,就會把不合理的布局給你撈出來,默認(rèn)view數(shù)量是80,深度是10,超過會報warning警告,也可以自定義lint規(guī)則來調(diào)整。
Layout Inspector:
當(dāng)然定位到具體代碼,可以借助Layout Inspector,使用方法參考:性能優(yōu)化工具(六)-Layout Inspector
調(diào)試GPU過度繪制 & GPU呈現(xiàn)模式:
調(diào)試GPU過度繪制。使用方法參考:性能優(yōu)化工具(七)-調(diào)試GPU過度繪制 & GPU呈現(xiàn)模式分析
四、優(yōu)化方案:
1)減少層級 (布局層級越少,加載速度越快)
布局容器選擇:在不增加層級的情況下,能用LinearLayout的就盡量別用RelativeLayout,在使用LinearLayout會增加層級的情況下,那就盡量用RelativeLayout。原因是RelativeLayout允許子View橫向和縱向相互依賴,那需要做兩次排序測量。但是就算這樣,也比增加層級要好。
合理使用Merge:自定義控件的rootView 或者 include 的xml, 如果最外層的rootView本身沒有太多意義,且內(nèi)部布局相對簡單,可以嘗試用merge來替代最外層相對簡單的FrameLayout和LinearLayout.復(fù)雜布局不建議使用,merge本身的xml屬性可能做不到。
2)減少同一層級控件數(shù)量 (控件數(shù)量越少,加載速度越快)
ViewStub的使用,實(shí)現(xiàn)部分布局的懶加載。解決動態(tài)加載布局的場景,減少2選1,N選1的場景的布局冗余,因?yàn)榫退悴伙@示,仍然會解析和測量這些布局。但是要注意使用特點(diǎn):程序的運(yùn)行期間,某個布局在被Inflate后,就不會有變化,才可以考慮用ViewStub。如果動態(tài)切來切去的那就沒法用。
3)減少和優(yōu)化控件屬性 (屬性越少,解析越快)
去掉控件的無用屬性
4)避免過度繪制
過度繪制指的是屏幕上某個像素點(diǎn)在同一幀的繪制時間內(nèi),被繪制了多次。
布局上:移除xml中非必須背景,移除window默認(rèn)背景 onCreate中 設(shè)置this.getWindow().setBackgroundDrawable(null);
自定義控件上:onDraw里同一區(qū)域被繪制多次,可以使用canvas.clipRect()來優(yōu)化組件繪制的重疊部分,這個算是摳細(xì)節(jié)了,大部分情況下都不會使用。
5 ) 主線程耗時導(dǎo)致無法按時繪制完成
主線程不做耗時操作,onDraw不做耗時操作。
可以使用 AsyncLayoutInflater 異步加載布局:
private void asyncInflated() {
TextView textView = (TextView) findViewById(R.id.tv_async);
final ViewGroup root = (ViewGroup) findViewById(R.id.ll_root);
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AsyncLayoutInflater asyncLayoutInflater = new AsyncLayoutInflater(OptActivity.this);
asyncLayoutInflater.inflate(R.layout.layout_async, root, new AsyncLayoutInflater.OnInflateFinishedListener() {
@Override
public void onInflateFinished(View view, int resId, ViewGroup parent) {
parent.addView(view);
}
});
}
});
}
最后談?wù)勼w會,這部分的優(yōu)化,其實(shí)主要還是代碼規(guī)范的問題,一般來說在寫代碼的時候注意以上幾點(diǎn),基本上問題就不大,除非要極致的扣細(xì)節(jié)那可能就還不夠,另外對于項目管理來說,可以寫一個比較好的lint module來幫助檢測項目規(guī)范,現(xiàn)在發(fā)現(xiàn)Lint確實(shí)非常實(shí)用。另外,還推薦下findBugs 這個插件,也非常不錯。
自定義Lint參考文章:
Android自定義Lint實(shí)踐
Android Studio 工具:Lint 代碼掃描工具(含自定義lint