當我們感覺到的流暢畫面,需要的畫面幀數要到達40幀到60幀每秒。而1幀的時間大約是16.67ms,換句話說,在1000ms的時間內,16.67ms大約就是現實60幀畫面的單位時間。在Android系統中,系統是通過VSYNC信號觸發對UI的渲染的,如果系統每次渲染的事件都保持在16.67ms之內,那末我們看到的UI界面將是非常的流暢的,這也就需要我們將所有程序的邏輯都保證在16ms以內,如果不能在16ms內完成繪制,那末就將造成丟幀的現象。即當前該重繪的幀被未處理完成的邏輯阻塞,例如1次繪制任務耗時20ms,那末在16ms系統發出的VSYNC信號就沒法繪制,該幀就會被拋棄,等待下次信號擦次開始繪制,這就是畫面卡頓的緣由。
Android系統提供了檢測UI渲染時間的工具,在“開發者選項中”有“GPU顯現模式分析”,選擇“在屏幕上顯示為條形圖”,以下所示(本測試機為魅族,其他手機可能略有不同):
每個條形圖都包括有3部份,藍色部份表示丈量繪制Display List的時間,紅色代表的是OpenGL渲染Display List所需要的時間,黃色代表的是CPU等待GPU處理的時間,中間的綠色橫線代表的是VSYNC時間16ms,需要盡可能將所有條形圖都控制在這條綠線之下。
overDraw表示的就是過度繪制,是指在1幀的時間內(16.67ms)像素被繪制了屢次,理論上1個像素每次只繪制1次是最優的,但是由于堆疊的布局致使1些像素會被屢次繪制,而每次繪制都會對應到CPU的1組繪圖命令和GPU的1些操作,造成CPU和GPU資源的浪費。在系統默許的繪制Activity的背景,如果再給布局繪制了堆疊的背景,那末默許Activity的背景就是無效的過度繪制。
在我們的“開發者選項”中有這樣1個檢測工具“調用GPU過度繪制”,激活該功能以后可以通過界面上的色彩來判斷overDraw的次數。
這個工具可以幫助我們檢測當前區域的繪制次數,從而優化界面繪圖層次,盡可能增大藍色的區域,減少紅色的區域。
在Android中,系統對View進行丈量,布局和繪制時,都是通過對View數的遍歷來進行操作的。如果1個View樹的高度太高,就會嚴重影響到丈量,布局和繪制的速度,因此,優化布局的第1個方法就會是下降View樹的高度,Google也在API文檔中建議View樹的高度不宜超過10層。在現在的XML文件的根布局中,我們默許RelativeLayout來替換使用LineraLayout作為默許的根布局,其緣由就是通過扁平的RelativeLayout來下降LineraLayout嵌套所產生的布局樹的高度,從而提高UI的渲染速度。
在1個利用程序的界面上,為了保持風格的統1,很多界面都會存在共通的UI,比如所說Topbar,Bottombar,Actionbar等等,如果在每個界面上都進行賦值這1段共通的布局代碼,不但不利于后期代碼的保護,還會增加程序的冗余。這時候候就能夠使用include標簽來定義1這1個共通的UI。
<?xml version="1.0" encoding="utf⑻"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dp"
android:layout_height="0dp"
android:textSize="28sp"
android:text="這是共通的UI界面"
android:gravity="center">
</TextView>
在該共通的布局中,我將layout_width和layout_height設置為0dp,這樣就迫使調用者在使用時必須對控件的狂傲進行賦值,否者是沒法看見該控件的。
下1步就是如何使用該共通的UI布局了。只需要在使用該共通的UI布局文件中使用include標簽的layout屬性對這個共通的UI的ID的援用便可。
<?xml version="1.0" encoding="utf⑻"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/commen_ui"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="20sp"
android:text="你好你好你好"/>
</LinearLayout>
這時候候如果我們要對布局中的屬性進行賦值,就要重新覆蓋某1項屬性,進行賦值便可。效果以下:
使用ViewStub標簽來實現對1個view的援用并且實現延遲加載。ViewStub是1個非常輕量級的組件,它不但不可視,而且大小為0,下面來演示如何使用ViewStub來進行實現延遲加載的目的。
首先創建1個布局,這個布局在初始化加載時是不需要顯示的,只有在某些情況下才需要進行顯示的,例如查看用戶信息的時候,只有點擊了某1個按鈕時,用戶詳細信息才顯示出來。
當運行程序后,我們發現ViewStub中的布局確切沒有顯示出來,那末要如何才能重新加載顯示的布局呢?
首先要通過findViewById()方法找到組件ViewStub。
mStub = (ViewStub) findViewById(R.id.vs_viewstub);
接下來就有兩種方式顯示這個view:
mStub.setVisibility(View.VISIBLE);
View inflate = mStub.inflate();
這兩種方式都是可以將ViewStub重新進行展開,顯示援用的布局,而唯1的區分在于就是inflate()可以返回援用的布局,從而可以通過View.findViewById()方法來找到對應的控件。
View inflate = mStub.inflate();
TextView textview = (TextView) inflate.findViewById(R.id.textview);
textview.setText("我是點擊后加載的");
注意:不管只用那種方式,1旦ViewStub被設置可見或是inflate以后,ViewStub就不存在了,不能被反復的inflate,取而代之的就是被inflate的Layout,并將這個Layout的ID重新設置為ViewStub中通過android:inflateId屬性所指定的ID,這也就是為何兩次點擊以后會報錯的緣由:以下所示:
Caused by: java.lang.IllegalStateException: ViewStub must have a non-null ViewGroup viewParent
扼要說明View.GONE和ViewStub標簽的區分是甚么?共同點都是初始時都不會顯示,但是ViewStub標簽只會在顯示時才會去渲染全部布局,而View.GONE,在初始化布局樹的時候就已添加在布局樹文件中了,相比之下ViewStub的效力會更高。
以下圖所示:
hierarchyviewer.bat是沒法在真機上進行使用的,只能在摹擬器上使用或是原生的摹擬器上使用,也就是沒有加密的裝備上。固然真機上也是可以用的,可以到github上下載1個開源項目View Server,下面在摹擬器上使用hierarchyviewer.bat。
hierarchyviewer.bat位于sdk\tools目錄下,直接雙擊便可啟動,以下:
注意:我們在使用hierarchyviewer的時候,1定要是摹擬器是打開的,這樣才能在hierarchyviewer中看到我們要顯示的布局文件。
下面我們寫1個非常冗余的布局進行顯示文件,代碼以下所示:
<?xml version="1.0" encoding="utf⑻"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="你好啊啊 啊啊啊啊 啊"
android:textColor="#efff0019"
android:textSize="20sp"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
這個布局文件是3層LinearLayout嵌套以后里面裝了1個button,很明顯這些LinearLayout都是冗余的,利用hierarchyviewer可以打開這個布局文件,以下圖所示:
通常情況下,我們只關注ID為content的Framlayout的分支,這也是setContentView()設置的內容,可以很明顯的看出在layout布局文件中(紅色布局),這3層LinearLayout沒有任何的分支,說明是冗余嵌套,可以直接去掉的。
當點擊其中1個view的時候,可以顯示view的繪制情況的,不過第1次點擊的時候各種顯示的事件都是n/a,需要點擊菜單中的Profile Node按鈕重新進行計算,才能回去到繪制信息。在系統的右下方會給出不同色彩的小圓點,用來表示繪制的效力,綠黃紅分別代表的是好中差的繪制效力。