Android佈局優化
Android的UI渲染機制
以前的彩色電視機,它的標準是每秒50幀畫面,看上去非常的流暢。其實人眼感覺到的流暢的畫面,需要每秒40–60幀畫面。在android中,系統通過VSYNC信號觸發對UI的渲染/重繪,UI重繪的時間是16ms,即1000ms/60幀。畫面的卡頓就是丟幀,邏輯阻塞造成重繪未完成。
在Android開發者選項當中,選擇GPU呈現模式分析,並選中在屏幕上顯示爲條線狀,如下圖所示:
每一條包含三個部分,最底部的藍色是測量繪製的Display List的時間,上面紅色代表OpenGL渲染Display List所需要的時間,在上面黃色CPU等待GPU的時間。最後的那條綠色橫線是系統VSYNC時間16ms,儘量所有的條形試圖都控制在這條綠色橫線之下。
避免OverDraw
過度繪製浪費很多的CPU/GPU資源,Android系統開發者選項中調試GPU過度繪製,並選中顯示過度繪製區域(PS:只有android4.2及以上的版本才具備此功能)
顏色標識: 從好到差:藍-綠-淡紅-紅
- 藍色1x過度繪製
- 綠色2x過度繪製
- 淡紅色3x過度繪製
- 紅色超過4x過度繪製
驗收標準:
- 控制過度繪製爲2x
- 不允許存在4x過度繪製
- 不允許存在面積超過屏幕1/4區域的3x過度繪製(淡紅色區域)
避免過度繪製的方法:
1. 在佈局層次一樣的情況下, 建議使用LinearLayout代替RelativeLayout, 因爲LinearLayout性能要稍高一點.
2. 在完成相對較複雜的佈局時,建議使用RelativeLayout,RelativeLayout可以簡單實現LinearLayout嵌套才能實現的佈局.
3. 將可複用的組件抽取出來並通過include標籤使用;
4. 使用ViewStub標籤來加載一些不常用的佈局;
5. 動態地inflation view性能要比SetVisiblity性能要好.當然用VIewStub是最好的選擇.
6. 使用merge標籤減少佈局的嵌套層次
7. 去掉多餘的背景顏色
8. 對於有多層背景顏色的Layout來說,留最上面一層的顏色即可,其他底層的顏色都可以去掉
9. 對於使用Selector當背景的Layout(比如ListView的Item,會使用Selector來標記點擊,選擇等不同的狀態),可以將normal狀態的color設置爲”@android:color/transparent”,來解決對應的問題
10. 內嵌使用包含layout_weight屬性的LinearLayout會在繪製時花費昂貴的系統資源,因爲每一個子組件都需要被測量兩次。在使用ListView與GridView的時候,這個問題顯的尤其重要,因爲子組件會重複被創建.所以要儘量避免使用Layout_weight
11. 使得Layout寬而淺,而不是窄而深(在Hierarchy Viewer的Tree視圖裏面體現)
佈局優化
佈局優化的思想很簡單,就是儘量減少佈局文件的層級,佈局層級少了,自然繪製的時間就短了。
另外的一種方案就是採用用include標籤/merge標籤和ViewStub。
- include標籤來重用layout代碼,可以將一個指定的文件加載到當前的佈局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/all_bg"
android:clipChildren="false"
tools:context="com.centraltide.tv.suixin.MainActivity">
<RelativeLayout
android:layout_weight="17"
android:layout_width="match_parent"
android:layout_height="0dp">
<include
android:id="@+id/rlTitle"
android:layout_width="match_parent"
android:layout_height="match_parent"
layout="@layout/activity_base_title_layout"
/>
<ImageView
android:id="@+id/ivMessage"
android:layout_width="wrap_content"
android:focusable="true"
android:focusableInTouchMode="true"
android:layout_centerHorizontal="true"
android:layout_height="wrap_content" />
...
</RelativeLayout>
</LinearLayout>
在include標籤只支持android:layout_*開頭的屬性,android:id這是個特例
- 減少視圖層級merge
merge標籤在UI的結構優化中起着非常重要的作用,它可以刪減多餘的層級,優化UI。merge多用於替換FrameLayout或者當一個佈局包含另一個時,merge標籤消除視圖層次結構中多餘的視圖組。例如你的主佈局文件是垂直佈局,引入了一個垂直佈局的include,這是如果include佈局使用的LinearLayout就沒意義了,使用的話反而減慢你的UI表現。這時可以使用merge標籤優化。
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/add"/>
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/delete"/>
</merge>
現在,當你添加該佈局文件時(使用include標籤),系統忽略merge節點並且直接添加兩個Button。
- ViewStub
ViewStub繼承至View,它非常輕量級且寬高都是0,因此它不參與任何的佈局和繪製。在實際開發中,很多佈局文件正常情況下不會顯示,沒有必要再整個界面初始化的時候加載進來,使用他並不會影響UI初始化時的性能。各種不常用的佈局想進度條、顯示錯誤消息等可以使用ViewStub 標籤,以減少內存使用量,加快渲染速度。通過ViewStub就可以做到在使用的時候在加載。
<ViewStub
android:id="@+id/stub_import"
android:inflatedId="@+id/panel_import"
android:layout="@layout/progress_overlay"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom" />
當你想加載佈局時,可以使用下面其中一種方法:
((ViewStub)findViewById(R.id.stub_import)).setVisibility(View.VISIBLE);
// or
View importPanel = ((ViewStub)findViewById(R.id.stub_import)).inflate();
當調用inflate()函數的時候,ViewStub被引用的資源替代,並且返回引用的view。 這樣程序可以直接得到引用的view而不用再次調用函數findViewById()來查找了。
注:ViewStub目前有個缺陷就是還不支持merge標籤。
今天多一份拼搏、明天多幾份歡笑。tks