性能優(yōu)化實(shí)踐(二)-布局優(yōu)化

一、簡介

眾所周知的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ī)則:

image.png

如果勾線了這兩項的話,那么你運(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容