Android的效能最佳化(上)

來源:互聯網
上載者:User

標籤:

1.Android UI的渲染機制

當我們感覺到的流暢畫面,需要的畫面幀數要達到40幀到60幀每秒。而一幀的時間大約是16.67ms,換句話說,在1000ms的時間內,16.67ms大約就是現實60幀畫面的單位時間。在Android系統中,系統是通過VSYNC訊號觸發對UI的渲染的,如果系統每次渲染的事件都保持在16.67ms以內,那麼我們看到的UI介面將是非常的流暢的,這也就需要我們將所有程式的邏輯都保證在16ms之內,如果不能在16ms內完成繪製,那麼就將造成丟幀的現象。即當前該重繪的幀被未處理完成的邏輯阻塞,例如一次繪製任務耗時20ms,那麼在16ms系統發出的VSYNC訊號就無法繪製,該幀就會被丟棄,等待下次訊號擦次開始繪製,這就是畫面卡頓的原因。

Android系統提供了檢測UI渲染時間的工具,在“開發人員選項中”有“GPU呈現模式分析”,選擇“在螢幕上顯示為橫條圖”,如下所示(本測試機為魅族,其他手機可能略有不同):

每一個橫條圖都包含有三部分,藍色部分表示測量繪製Display List的時間,紅色代表的是OpenGL渲染Display List所需要的時間,黃色代表的是CPU等待GPU處理的時間,中間的綠色橫線代表的是VSYNC時間16ms,需要盡量將所有橫條圖都控制在這條綠線之下。

2.overDraw

overDraw表示的就是過度繪製,是指在一幀的時間內(16.67ms)像素被繪製了多次,理論上一個像素每次只繪製一次是最優的,但是由於重疊的布局導致一些像素會被多次繪製,而每次繪製都會對應到CPU的一組繪圖命令和GPU的一些操作,造成CPU和GPU資源的浪費。在系統預設的繪製Activity的背景,如果再給布局繪製了重疊的背景,那麼預設Activity的背景就是無效的過度繪製。

在我們的“開發人員選項”中有這樣一個偵查工具“調用GPU過度繪製”,啟用該功能之後可以通過介面上的顏色來判斷overDraw的次數。

這個工具可以協助我們檢測目前範圍的繪製次數,從而最佳化介面繪圖層次,盡量增大藍色的地區,減少紅色的地區。

3.最佳化布局層次

在Android中,系統對View進行測量,布局和繪製時,都是通過對View數的遍曆來進行操作的。如果一個View樹的高度太高,就會嚴重影響到測量,布局和繪製的速度,因此,最佳化布局的第一個方法就會是降低View樹的高度,Google也在API文檔中建議View樹的高度不宜超過10層。在現在的XML檔案的根布局中,我們預設RelativeLayout來替換使用LineraLayout作為預設的根布局,其原因就是通過扁平的RelativeLayout來降低LineraLayout嵌套所產生的布局樹的高度,從而提高UI的渲染速度。

1.使用 include 標籤重用Layout

在一個應用程式的介面上,為了保持風格的統一,很多介面都會存在共通的UI,比如所說Topbar,Bottombar,Actionbar等等,如果在每一個介面上都進行賦值這一段共通的布局代碼,不僅不利於後期代碼的維護,還會增加程式的冗餘。這時候就可以使用include標籤來定義一這一個共通的UI。

<?xml version="1.0" encoding="utf-8"?><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,這樣就迫使調用者在使用時必須對控制項的狂傲進行賦值,否者是無法看見該控制項的。

下一步就是如何使用該共通的UI布局了。只需要在使用該共通的UI布局檔案中使用include標籤的layout屬性對這個共通的UI的ID的引用即可。

<?xml version="1.0" encoding="utf-8"?><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>

這時候如果我們要對布局中的屬性進行賦值,就要重新覆蓋某一項屬性,進行賦值即可。效果如下:

2. 使用ViewStub實現view的消極式載入

使用ViewStub標籤來實現對一個view的引用並且實現消極式載入。ViewStub是一個非常輕量級的組件,它不僅不可視,而且大小為0,下面來示範如何使用ViewStub來進行實現消極式載入的目的。

首先建立一個布局,這個布局在初始化載入時是不需要顯示的,只有在某些情況下才需要進行顯示的,例如查看使用者資訊的時候,只有點擊了某一個按鈕時,使用者詳細資料才顯示出來。
當運行程式後,我們發現ViewStub中的布局確實沒有顯示出來,那麼要如何才能重新載入顯示的布局呢?
首先要通過findViewById()方法找到組件ViewStub。

mStub = (ViewStub) findViewById(R.id.vs_viewstub);
接下來就有兩種方式顯示這個view:
  1. VISIBLE
    通過調用ViewStub的setVisiblity()方法來顯示這個view.代碼如下:
mStub.setVisibility(View.VISIBLE);
  1. INFLATE
    通過調用ViewStub的inflate()方法來顯示這個view.代碼如下:
View inflate = mStub.inflate();

這兩種方式都是可以將ViewStub重新進行展開,顯示引用的布局,而唯一的區別在於就是inflate()可以返回引用的布局,從而可以通過View.findViewById()方法來找到對應的控制項。

View inflate = mStub.inflate();        TextView textview = (TextView) inflate.findViewById(R.id.textview);        textview.setText("我是點擊後載入的");

注意:不管只用那種方式,一旦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的效率會更高。
如所示:

4.hierarchyviewer.bat

hierarchyviewer.bat是無法在真機上進行使用的,只能在模擬器上使用或者是原生的模擬器上使用,也就是沒有加密的裝置上。當然真機上也是可以用的,可以到github上下載一個開源項目View Server,下面在模擬器上使用hierarchyviewer.bat。
hierarchyviewer.bat位於sdk\tools目錄下,直接雙擊即可啟動,如下:

注意:我們在使用hierarchyviewer的時候,一定要是模擬器是開啟的,這樣才能在hierarchyviewer中看到我們要顯示的布局檔案。

下面我們寫一個非常冗餘的布局進行顯示檔案,代碼如下所示:

<?xml version="1.0" encoding="utf-8"?><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>

這個布局檔案是三層LinearLayout嵌套之后里面裝了一個button,很顯然這些LinearLayout都是冗餘的,利用hierarchyviewer可以開啟這個布局檔案,如所示:


通常情況下,我們只關注ID為content的Framlayout的分支,這也是setContentView()設定的內容,可以很明顯的看出在layout布局檔案中(紅色布局),這三層LinearLayout沒有任何的分支,說明是冗餘嵌套,可以直接去掉的。

當點擊其中一個view的時候,可以顯示view的繪製情況的,不過第一次點擊的時候各種顯示的事件都是n/a,需要點擊菜單中的Profile Node按鈕重新進行計算,才能回去到繪製資訊。在系統的右下方會給出不同顏色的小圓點,用來表示繪製的效率,綠黃紅分別代表的是好中差的繪製效率。

Android的效能最佳化(上)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.