相關文章
Android繪製優化(一)繪製性能分析javascript
咱們知道一個界面的測量和繪製是經過遞歸來完成的,減小布局的層數就會減小測量和繪製的時間,從而性能就會獲得提高。固然這只是佈局優化的一方面,那麼如何來進行佈局的分析和優化呢?本篇文章會給你一個滿意的答案。java
在講到如何去佈局優化前,咱們先來學習兩種佈局優化的工具。android
Hierarchy Viewer是Android SDK自帶的可視化的調試工具,用來檢查佈局嵌套和繪製的時間。須要注意的是在在Android的官方文檔中提到:出於安全考慮,Hierarchy Viewer只能鏈接Android開發版手機或是模擬器。
首先咱們在Android Studio中選擇Tools->Android->Android Device Monitor,在Android Device Monitor中選擇Hierarchy Viewer ,以下圖所示:canvas
Hierarchy Viewer中有4個四個子窗口,它們的的做用爲:安全
根據上面講到的Hierarchy Viewer的4個四個子窗口,咱們能夠很容易的查看咱們佈局控件的層級關係。固然Hierarchy Viewer還能夠查看某一個View的耗時,咱們能夠選擇某一個View,而後單擊下圖紅色箭頭標識的按鈕,這裏咱們把他簡稱爲Layout Time按鈕。 微信
從圖中能夠看出,被選中的LinearLayout給出了自身Measure、Layout和Draw的耗時,而且它所包含的View中都有了三個指示燈,分別表明當前View在Measure、Layout和Draw的耗時,綠色表明比其餘50%View的同階段(好比Measure階段)速度要快,黃色則表明比其餘50%View同階段速度要慢,紅色則表明比其餘View同階段都要慢,則須要注意了。若是想要看View的具體耗時,則點擊該View就能夠了。ide
Android lint是在ADT 16提供的新工具,它是一個代碼掃描工具,經過代碼靜態檢查來發現代碼出現的潛在問題,並給出優化建議。檢查的範圍主要有如下幾點:工具
Android Lint功能十分強大,這裏咱們只關注XML佈局檢查,咱們能夠經過Android Studio的Analyze->Inspect Code來配置檢查的範圍,以下圖所示。
佈局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent"> ...複製代碼
第三行的Namespace 確實沒有被用到。若是想要自定義Android Lint的檢查提示,能夠經過File->Settings->Editor->Inspections中來配置Android Lint,以下圖所示。
性能
佈局的優化方法不少,主要包括合理運用佈局、Include、Merge、ViewStub,下面咱們來一一對這些內容進行講解。
合理運用佈局
咱們經常使用的佈局主要有LinearLayout、RelativeLayout和FrameLayout等,合理的使用它們可使得Android繪製工做量變少,性能獲得提升。咱們來舉個簡單的例子。
<?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="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="佈局優化" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Merge" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ViewStub" />
</LinearLayout>
</LinearLayout>複製代碼
上面的代碼用了兩個LinearLayout來進行佈局,運行效果以下圖所示。
能夠看到咱們的佈局共有3層,一共含有5個View。若是咱們用RelativeLayout來進行改寫呢?代碼以下所示。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="佈局優化" />
<TextView
android:id="@+id/tv_text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@id/tv_text1"
android:text="Merge" />
<TextView
android:id="@+id/tv_text3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_text2"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@+id/tv_text1"
android:text="ViewStub" />
</RelativeLayout>複製代碼
咱們只用了一個RelativeLayout來進行佈局。用Hierarchy Viewer來查看層級狀況,以下圖所示。
佈局共有兩層,一共含有4個View。從這裏咱們就能夠看出咱們用RelativeLayout減小了一層的佈局,固然這只是一個簡單例子,若是佈局複雜,那麼合理的用RelativeLayout來替代LinearLayout會減小不少層佈局。
通常狀況下,RelativeLayout的性能是比LinearLayout低,由於RelativeLayout中的View的排列方式是基於彼此依賴的。可是若是佈局層數較多時,若是能用RelativeLayout來實現,仍是推薦用RelativeLayout。
一個很常見的場景就是,多個佈局須要複用一個相同的佈局,好比一個TitleBar。若是這些界面都要加上這個相同佈局TitleBar,維護起來就就很麻煩,咱們須要複製TitleBar的佈局到每一個須要添加的界面,這樣容易發生遺漏。若是須要修改TitleBar則須要去每一個引用TitleBar的佈局進行修改。爲了解決這些問題,咱們能夠用Include標籤來解決。
首先咱們先來寫一個簡單的TitleBar佈局:titlebar.xml 以下所示。
<?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="40dp"
android:background="@android:color/darker_gray">
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/ico_left"
android:padding="3dp"
android:layout_gravity="center"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="繪製優化" />
</LinearLayout>複製代碼
這個TitleBar由ImageView和TextView組成,下面咱們將TitleBar引入到咱們此前用過的佈局中,以下所示。
<?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">
<include layout="@layout/titlebar" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="佈局優化" />
...
</RelativeLayout>
</LinearLayout>複製代碼
能夠看到咱們用include標籤引入了titlebar佈局,運行效果以下圖所示。
Merge意味着合併,在合適的場景使用Merge標籤能夠減小多餘的層級。Merge標籤通常和Include標籤搭配使用,上面的例子,咱們用Hierarchy Viewer來查看佈局層級,以下圖所示。
能夠看到咱們用Include標籤引用的佈局的根佈局是一個LinearLayout。若是咱們使用Merge標籤來替換LinearLayout呢?titlebar.xml 的代碼以下所示。
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="@android:color/darker_gray
>
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/ico_left"
android:padding="3dp"
android:layout_gravity="center"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="繪製優化" />
</merge>複製代碼
這時咱們再用Hierarchy Viewer來查看佈局層級,以下圖所示。
<?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">
<com.example.liuwangshu.moonlayout.TitleBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="@android:color/darker_gray">
</com.example.liuwangshu.moonlayout.TitleBar>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="佈局優化" />
...
</RelativeLayout>
</LinearLayout>複製代碼
上面佈局中TitleBar就是一個自定義View,它繼承LinearLayout。咱們在TitleBar標籤中添加此前的LinearLayout的屬性:android:orientation和android:background。
一個很常見的開發場景就是咱們想要一個佈局時,並非全部的控件都須要顯示出來,而是顯示出一部分,對於這種狀況,咱們通常採用的方法就是使用View的GONE和INVISIBLE,可是這種方法效率不高,雖然是達到了隱藏的目的,可是仍在佈局當中,系統仍然會解析它們,咱們能夠用ViewStub來解決這一問題。
ViewStub是輕量級的View,不可見而且不佔佈局位置。當ViewStub調用inflate方法或者設置可見時,系統會加載ViewStub指定的佈局,而後將這個佈局添加到ViewStub中,所以,在對ViewStub調用inflate方法或者設置可見時,它是不佔佈局空間和系統資源的,它主要的目的就是爲目標視圖佔用一個位置。所以,使用ViewStub能夠提升界面初始化的性能,從而提升界面的加載速度。
咱們首先在佈局中加入ViewStub標籤,佈局代碼以下所示。
<?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"> <ViewStub android:id="@+id/viewsub" android:layout_width="match_parent" android:layout_height="40dp" android:layout="@layout/titlebar"/> ... </LinearLayout>複製代碼
ViewStub標籤中用android:layout引用了此前寫好的佈局titlebar.xml。這時咱們運行程序,ViewStub標籤所引用的佈局是顯示不出來的,由於引該局尚未加載到ViewStub中,接下來在代碼中使用ViewStub:
public class MainActivity extends AppCompatActivity {
private ViewStub viewsub;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewsub= (ViewStub) findViewById(R.id.viewsub);
// viewsub.inflate();//1
viewsub.setVisibility(View.VISIBLE);//2
}
}複製代碼
可使用註釋1和註釋2處的代碼來將ViewStub引用的佈局加載到ViewStub中,這樣引用的佈局就顯示了出來。
在使用ViewStub時須要主要如下問題:
什麼是過分繪製呢?咱們來打個比方,假設你要粉刷房子的牆壁,一開始刷了綠色,接着又刷了黃色,這樣黃色就將綠色蓋住,也就說明第一次的大量粉刷工做白作了。一樣手機屏幕繪製也是如此,過分繪製是指在屏幕上某個像素在同一幀的時間內被繪製屢次,從而浪費了GPU和CPU的資源。產生這一緣由主要有兩個緣由:
過分繪製是不可避免的,可是過多的過分繪製會浪費不少資源,而且致使性能問題,所以,避免過分繪製是十分必要的。咱們能夠用Android系統中自帶的工具來檢測過分繪製。首先要保證系統版本在Android 4.1以上,接着在開發者選項中打開調試GPU過分繪製選項就能夠進入GPU過分繪製模式,以下圖所示。
這時屏幕會出現出各類顏色,主要有如下幾種,以下圖所示。
各個顏色的定義爲:
最理想的是藍色,一個像素只繪製一次,合格的頁面繪製是白色、藍色爲主,綠色以上區域不能超過整個的三分之一,顏色越淺越好。
避免過分繪製主要有如下幾個方案:
1.移除不須要的background。
2.在自定義View的OnDraw方法中,用canvas.clipRect來指定繪製的區域,防止重疊的組件發生過分繪製。
歡迎關注個人微信公衆號,第一時間得到博客更新提醒,以及更多成體系的Android相關原創技術乾貨。
掃一掃下方二維碼或者長按識別二維碼,便可關注。