主要影響Android應用中頁面顯示的速度。1個頁面經過遞歸 完成測量 & 繪製過程 = measure、layout 過程,而這個過程過長則會給用戶帶來卡頓的視覺效果。android
佈局優化的思路其實很簡單,就是儘可能減小布局文件的層級。佈局的層級少了,這就意味着Android繪製時工做量少了,那麼程序的性能天然就高了。性能優化
若是佈局中便可使用LinearLayout也可使用RelativeLayout,那就採用LinearLayout。由於RelativeLayout在繪製時須要對子View分別進行了豎直和水平方向的兩次測量,而Linearlayout在繪製時是根據咱們設置的方向分別調用不一樣的測量方法。注意一點若是LinearLayout中子View使用了layout_weight屬性時一樣須要對子View進行兩次測量以肯定最終大小(對此不瞭解的小夥伴們能夠查看源碼中onMeasure和onLayout方法本文就很少貼源碼)。LinearLayout和FrameLayout都是一種性能耗費低的佈局。可是不少時候單純經過一個LinearLayout或FrameLayout沒法實現產品的效果,須要經過嵌套的方式來完成。這種狀況下建議使用RelativeLayout,由於嵌套就至關於增長了佈局的層級,一樣會下降程序的性能。
bash
評論屢次提到Constraintlayout,因爲筆者用的較少忘了說,疏忽了,疏忽了[手動哭笑]。面對複雜度高的佈局(比RelativeLayout和LinearLayout屢次嵌套)Constraintlayout確實更簡單,繪製時間更短。但面對複雜度較低的佈局,RelativeLayout比ConstraintLayout在onMesaure階段快數倍。下圖爲Hierarchy Viewer的測試結果(裏面一個TextView,一個ImageView,關於Hierarchy Viewer的使用會在下文佈局調優工具中):服務器
使用 <include>標籤提取佈局間的公共部分,經過提升佈局的複用性從而減小測量 & 繪製時間網絡
<!--抽取出的公共佈局:include_title.xml-->
<?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"
android:background="@color/colorAccent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:paddingLeft="15dp"
android:src="@mipmap/ic_titilebar_back"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="@string/title"
android:textColor="@color/white"
android:textSize="18sp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:gravity="center"
android:padding="15dp"
android:text="@string/more"
android:textColor="@color/white"
android:textSize="16sp"/>
</RelativeLayout>
<!--佈局:activity_main.xl引用公共佈局include_title.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="match_parent"
android:orientation="vertical">
<include layout="@layout/include_title"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="Hello World!" />
</LinearLayout>
複製代碼
<include> 標籤只支持以android:layout_開頭的屬性(android:id除外),須要注意一點若是使用了android:layout_*這種屬性,那麼要求android:layout_width 和android:layout_height必須存在,不然其餘android:layout_*形式的屬性沒法生效,下面是一個指定了android:layout_*屬性的示例app
<include
android:id="@+id/include_title"
android:layout_width="match_parent"
android:layout_height="48dp"
layout="@layout/include_title"/>
複製代碼
<merge> 佈局標籤通常和 <include> 標籤一塊兒使用從而減小布局的層級。例如當前佈局是一個豎直方向的LinearLayout,這個時候若是被包含的佈局也採用了豎直方向的LinearLayout,那麼顯然被包含的佈局文件中的LinearLayout是多餘的,這時經過 <merge> 佈局標籤就能夠去掉多餘的那一層LinearLayout。以下所示:ide
<!--抽取出的公共佈局:include_title.xml-->
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
</merge>
<!--佈局:activity_main.xl引用公共佈局include_title.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="match_parent"
android:orientation="vertical">
<include layout="@layout/include_title"/>
</LinearLayout>
複製代碼
ViewStub繼承了View,它很是輕量級且寬和高都爲0,所以它自己不參與任何的繪製過程,避免資源的浪費,減小渲染時間,在須要的時候才加載View。所以ViewStub的意義在於按需求加載所需的佈局,在實際開發中,不少佈局在正常狀況下不會顯示,好比加載數據暫無數據,網絡異常等界面,這個時候就不必在整個界面初始化的時候將其加載進來,經過ViewStub就能夠作到在使用時在加載,提升了程序初始化時的性能。以下一個ViewStub的示例:工具
<!--暫無數據頁:empty_data.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="match_parent"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@mipmap/ic_empty_order"/>
<TextView
android:layout_below="@+id/iv_empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="暫無數據"/>
</LinearLayout>
<!--佈局activity_main.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="match_parent"
android:gravity="center"
android:orientation="vertical">
//view_stub是ViewStub的id,empty是empty_data.xml這個佈局根元素的id
<ViewStub
android:id="@+id/view_stub"
android:inflatedId="@+id/empty"
android:layout="@layout/empty_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
<!--MainActivity-->
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
//加載ViewStub中的佈局的兩種方式setVisibility或inflate
mViewStub.setVisibility(View.VISIBLE);
mViewStub.inflate();
}
複製代碼
使用ViewStub需注意:當ViewStub經過setVisibility或inflate方法加載後,ViewStub就會被它內部的佈局替換掉,這個時候ViewStub就再也不是佈局結構中的的一部分。目前ViewStub中的layout還不支持使用<merge> 標籤。佈局
佈局屬性 wrap_content 會增長佈局測量時計算成本,應儘量少用post
在繪製佈局中,某些狀況下咱們能夠省去部分控件的使用。下文介紹幾種常見的狀況:
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="20dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:gravity="center_vertical"
android:drawableLeft="@mipmap/icon_my_unlock" // 設置左邊顯示的icon
android:drawablePadding="10dp" // 設置icon和文本的間距
android:drawableRight="@mipmap/icon_right" // 設置右邊顯示的icon
android:text="@string/account_unlock"
/>
複製代碼
<?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:divider="@drawable/divider_line"
android:dividerPadding="16dp"
android:showDividers="middle"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:gravity="center_vertical"
android:drawableLeft="@mipmap/icon_my_unlock"
android:drawablePadding="10dp"
android:drawableRight="@mipmap/icon_right"
android:background="@color/color_FFFFFF"
android:text="@string/account_unlock"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:gravity="center_vertical"
android:drawableLeft="@mipmap/icon_my_unlock"
android:drawablePadding="10dp"
android:drawableRight="@mipmap/icon_right"
android:background="@color/color_FFFFFF"
android:text="@string/account_unlock"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:gravity="center_vertical"
android:drawableLeft="@mipmap/icon_my_unlock"
android:drawablePadding="10dp"
android:drawableRight="@mipmap/icon_right"
android:background="@color/color_FFFFFF"
android:text="@string/account_unlock"
/>
<!--Shape:divider_line.xml-->
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/colorPrimary"/>
<size android:height="1dp"/>
</shape>
</LinearLayout>
複製代碼
核心代碼就是對LinearLayout設置divider
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lineSpacingExtra="10dp"
android:text="@string/test_text"
android:textSize="20sp"
android:textColor="@color/colorPrimary"/>
複製代碼
經過lineSpacingExtra設置行間隔,test_text內容爲:
<string name="test_text">標題:%1$s\n時間:%2$s\n內容:%3$s</string>
複製代碼
在MainActivity中的使用
mText.setText(String.format(getResources().getString(R.string.test_text), "測試", "2018-8-9","測試測試"));
複製代碼
佔位符的使用方法:%n表示第n位要被替換的,$s表示字符串類型佔位符,$d表示整型佔位符,$f表示浮點型佔位符
在實際開發中哪怕注意了上述的優化方案,不免仍是會出現佈局性能的問題。這時咱們可以使用佈局調優工具來分析問題。本文將介紹經常使用的幾種工具。
Lint 是Android Studio 提供的 代碼掃描分析工具,它能夠幫助咱們發現代碼結構/質量問題,同時提供一些解決方案,並且這個過程不須要咱們手寫測試用例。 Lint 的使用路徑: 工具欄 -> Analyze -> Inspect Code
默認是檢查整個項目,咱們能夠點擊 Custom scope 自定義檢查範圍
固然你也能夠選擇特定的類進行檢查點擊下圖紅色箭頭標識的地方
點擊「+」號新增一個檢查範圍:
咱們左擊想掃描的文件,點擊右邊對應的按鈕。能夠看到文件邊色了,紅框顯示需掃描24個文件,點擊OK→OK
稍等一下子,會彈出 Inspection 對話框,顯示檢查結果。
Lint 的警告嚴重程度有如下幾種:
本文對Lint的介紹就到此若是對Lint想了解更多的小夥伴能夠點擊Lint
Hierarchy Viewer 是Android Studio 提供的UI性能檢測工具。可得到UI佈局設計結構 & 各類屬性信息,幫助咱們優化佈局設計 。
使用Hierarchy Viewer ,您的設備必須運行Android 4.1或更高版本。若是您是使用真機的話需注意如下兩點:
Hierarchy Viewer 的使用路徑: 工具欄 ->Tools->Android->Android Device Monitor(默認是顯示DDMS窗口,更改可點擊Open Perspective->Hierarchy Viewer,也能夠直接點擊 Hierarchy Viewer Button),以下圖所示:
更改後顯示Hierarchy Viewer的窗口以下圖所示:
若是您看的的視圖排列不同,可選擇 Window->Reset Perspective 返回默認佈局。下面介紹下基本窗口:
上圖圖標的使用左到右介紹:
這些點大體對應於處理管道的度量,佈局和繪製階段。點的顏色表示該節點相對於本地系列中全部其餘配置節點的相對性能。
所測量的是每一個節點相對於兄弟視圖的性能,所以配置文件中始終存在紅色節點,除非全部視圖執行相同,而且它並不必定意味着紅色節點執行得不好(僅限於它是最慢的視圖)在本地視圖組中)。
本文對Hierarchy Viewer的介紹就到此了,想了解更多的小夥伴能夠點擊Hierarchy Viewer
開發者選項是Android 系統爲開發者提供的一個APP驗證、調試、優化等各類功能的入口(需Android 4.1以上)。本文主要對GPU過分繪製菜單介紹,對其餘菜單功能有興趣的小夥伴能夠參考探索Android手機開發者選項
GPU過分繪製是指在屏幕上某個像素在同一幀的時間內被繪製屢次,產生的主要緣由有兩個:
不一樣手機開發商都對手機界面作了定製化處理,打開手機開發者選項的方式各不相同,但基本的功能菜單都是相似的。因爲筆者如今用的是小米5s Plus,所以打開方式是設置->個人設備->所有參數->連續點擊MIUI版本,提示開發者模式開啓便可(不一樣MIUI版本開啓路徑也不同)。而後設置->更多設置,高級設置或者其餘設置等等菜單中便可看到開發者選項的菜單了。
在開發者選項中點擊調試GPU過分繪製,以下圖所示
這時屏幕會出現各類顏色,每種顏色的定義爲:
以上就是本文對佈局調優工具的介紹,固然還有其餘佈局調優工具如Systrace本文就不一一介紹了。
本文主要講解Android 性能優化中的 佈局優化。如對小夥伴們有幫助,麻煩給個喜歡,不足之處請留言私聊點出謝謝!
對Flutter有興趣的可參考Flutter學習之路(一)Flutter簡介及Window下開發環境搭建