一、簡介
眾所周知的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ī)則:
如果勾線了這兩項(xiàng)的話蔽挠,那么你運(yùn)行 Analyze/Inspect Code的時候僧鲁,就會把不合理的布局給你撈出來,默認(rèn)view數(shù)量是80象泵,深度是10寞秃,超過會報(bào)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é)那可能就還不夠,另外對于項(xiàng)目管理來說,可以寫一個比較好的lint module來幫助檢測項(xiàng)目規(guī)范弦牡,現(xiàn)在發(fā)現(xiàn)Lint確實(shí)非常實(shí)用。另外椭豫,還推薦下findBugs 這個插件,也非常不錯框都。
自定義Lint參考文章:
Android自定義Lint實(shí)踐
Android Studio 工具:Lint 代碼掃描工具(含自定義lint