效能最佳化之布局最佳化,效能最佳化布局

來源:互聯網
上載者:User

(轉載)效能最佳化之布局最佳化,效能最佳化布局


來源:http://www.trinea.cn/android/layout-performance/

本文為Android效能最佳化的第二篇——布局最佳化,主要介紹使用抽象布局標籤(include, viewstub, merge)、去除不必要的嵌套和View節點、減少不必要的infalte及其他Layout方面可調優點,順帶提及布局調優相關工具(hierarchy viewer和lint)

 

效能最佳化專題已完成五部分:

效能最佳化總綱——效能問題及效能調優方式
效能最佳化第三篇——Java(Android)代碼最佳化
效能最佳化第二篇——布局最佳化
效能最佳化第一篇——資料庫效能最佳化

效能最佳化執行個體 

 

1、抽象布局標籤 

(1) <include>標籤
include標籤常用於將布局中的公用部分提取出來供其他layout共用,以實現布局模組化,這在布局編寫方便提供了大大的便利。
下面以在一個布局main.xml中用include引入另一個布局foot.xml為例。main.mxl代碼如下:

 
 1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3     android:layout_width="match_parent" 4     android:layout_height="match_parent" > 5   6     <ListView 7         android:id="@+id/simple_list_view" 8         android:layout_width="match_parent" 9         android:layout_height="match_parent"10         android:layout_marginBottom="@dimen/dp_80" />11  12     <include layout="@layout/foot.xml" />13  14 </RelativeLayout>

其中include引入的foot.xml為公用的頁面底部,代碼如下:

 
 1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3     android:layout_width="match_parent" 4     android:layout_height="match_parent" > 5   6     <Button 7         android:id="@+id/button" 8         android:layout_width="match_parent" 9         android:layout_height="@dimen/dp_40"10         android:layout_above="@+id/text"/>11  12     <TextView13         android:id="@+id/text"14         android:layout_width="match_parent"15         android:layout_height="@dimen/dp_40"16         android:layout_alignParentBottom="true"17         android:text="@string/app_name" />18  19 </RelativeLayout>

<include>標籤唯一需要的屬性是layout屬性,指定需要包含的布局檔案。可以定義android:id和android:layout_*屬性來覆蓋被引入布局根節點的對應屬性值。注意重新定義android:id後,子布局的頂結點i就變化了。

 

(2) <viewstub>標籤
viewstub標籤同include標籤一樣可以用來引入一個外部布局,不同的是,viewstub引入的布局預設不會擴張,即既不會佔用顯示也不會佔用位置,從而在解析layout時節省cpu和記憶體。
viewstub常用來引入那些預設不會顯示,只在特殊情況下顯示的布局,如進度布局、網路失敗顯示的重新整理布局、資訊出錯出現的提示布局等。
下面以在一個布局main.xml中加入網路錯誤時的提示頁面network_error.xml為例。main.mxl代碼如下:

 
 1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3     android:layout_width="match_parent" 4     android:layout_height="match_parent" > 5   6     …… 7     <ViewStub 8         android:id="@+id/network_error_layout" 9         android:layout_width="match_parent"10         android:layout_height="match_parent"11         android:layout="@layout/network_error" />12  13 </RelativeLayout>

其中network_error.xml為只有在網路錯誤時才需要顯示的布局,預設不會被解析,範例程式碼如下:

 
 1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3     android:layout_width="match_parent" 4     android:layout_height="match_parent" > 5   6     <Button 7         android:id="@+id/network_setting" 8         android:layout_width="@dimen/dp_160" 9         android:layout_height="wrap_content"10         android:layout_centerHorizontal="true"11         android:text="@string/network_setting" />12  13     <Button14         android:id="@+id/network_refresh"15         android:layout_width="@dimen/dp_160"16         android:layout_height="wrap_content"17         android:layout_below="@+id/network_setting"18         android:layout_centerHorizontal="true"19         android:layout_marginTop="@dimen/dp_10"20         android:text="@string/network_refresh" />21  22 </RelativeLayout>

 

在java中通過(ViewStub)findViewById(id)找到ViewStub,通過stub.inflate()展開ViewStub,然後得到子View,如下:

 
 1 private View networkErrorView; 2   3 private void showNetError() { 4     // not repeated infalte 5     if (networkErrorView != null) { 6         networkErrorView.setVisibility(View.VISIBLE); 7         return; 8     } 9  10     ViewStub stub = (ViewStub)findViewById(R.id.network_error_layout);11     networkErrorView = stub.inflate();12     Button networkSetting = (Button)networkErrorView.findViewById(R.id.network_setting);13     Button refresh = (Button)findViewById(R.id.network_refresh);14 }15  16 private void showNormal() {17     if (networkErrorView != null) {18         networkErrorView.setVisibility(View.GONE);19     }20 }

 

在上面showNetError()中展開了ViewStub,同時我們對networkErrorView進行了儲存,這樣下次不用繼續inflate。這就是後面第三部分提到的減少不必要的infalte。

viewstub標籤大部分屬性同include標籤類似。

 上面展開ViewStub部分代碼

 
1 ViewStub stub = (ViewStub)findViewById(R.id.network_error_layout);2 networkErrorView = stub.inflate();

也可以寫成下面的形式

 
1 View viewStub = findViewById(R.id.network_error_layout);2 viewStub.setVisibility(View.VISIBLE);   // ViewStub被展開後的布局所替換3 networkErrorView =  findViewById(R.id.network_error_layout); // 擷取展開後的布局

效果一致,只是不用顯示的轉換為ViewStub。通過viewstub的原理我們可以知道將一個view設定為GONE不會被解析,從而提高layout解析速度,而VISIBLE和INVISIBLE這兩個可見度屬性會被正常解析。

 

(3) <merge>標籤
在使用了include後可能導致布局嵌套過多,多餘不必要的layout節點,從而導致解析變慢,不必要的節點和嵌套可通過hierarchy viewer(下面布局調優工具中有具體介紹)或設定->開發人員選項->顯示布局邊界查看。

 

merge標籤可用於兩種典型情況:
a.  布局頂結點是FrameLayout且不需要設定background或padding等屬性,可以用merge代替,因為Activity內容試圖的parent view就是個FrameLayout,所以可以用merge消除只剩一個。
b.  某布局作為子布局被其他布局include時,使用merge當作該布局的頂節點,這樣在被引入時頂結點會自動被忽略,而將其子節點全部合并到主布局中。

以(1) <include>標籤的樣本為例,用hierarchy viewer查看main.xml布局如:


可以發現多了一層沒必要的RelativeLayout,將foot.xml中RelativeLayout改為merge,如下:

 
 1 <?xml version="1.0" encoding="utf-8"?> 2 <merge xmlns:android="http://schemas.android.com/apk/res/android" 3     android:layout_width="match_parent" 4     android:layout_height="match_parent" > 5   6     <Button 7         android:id="@+id/button" 8         android:layout_width="match_parent" 9         android:layout_height="@dimen/dp_40"10         android:layout_above="@+id/text"/>11  12     <TextView13         android:id="@+id/text"14         android:layout_width="match_parent"15         android:layout_height="@dimen/dp_40"16         android:layout_alignParentBottom="true"17         android:text="@string/app_name" />18  19 </merge>

 

運行後再次用hierarchy viewer查看main.xml布局如:

這樣就不會有多餘的RelativeLayout節點了。

 

2、去除不必要的嵌套和View節點
(1) 首次不需要使用的節點設定為GONE或使用viewstub
(2) 使用RelativeLayout代替LinearLayout
大約在Android4.0之前,建立工程的預設main.xml中頂節點是LinearLayout,而在之後已經改為RelativeLayout,因為RelativeLayout效能更優,且可以簡單實現LinearLayout嵌套才能實現的布局。
4.0及以上Android版本可通過設定->開發人員選項->顯示布局邊界開啟頁面配置顯示,看看是否有不必要的節點和嵌套。4.0以下版本可通過hierarchy viewer查看。

 

3、減少不必要的infalte
(1) 對於inflate的布局可以直接緩衝,用全部變數代替局部變數,避免下次需再次inflate
如上面ViewStub樣本中的

 
1 if (networkErrorView != null) {2     networkErrorView.setVisibility(View.VISIBLE);3     return;4 }

 

(2) ListView提供了item緩衝,adapter getView的標準寫法,如下:

 1 @Override 2 public View getView(int position, View convertView, ViewGroup parent) { 3     ViewHolder holder; 4     if (convertView == null) { 5         convertView = inflater.inflate(R.layout.list_item, null); 6         holder = new ViewHolder(); 7         …… 8         convertView.setTag(holder); 9     } else {10         holder = (ViewHolder)convertView.getTag();11     }12 }13  14 /**15 * ViewHolder16 *17 * @author trinea@trinea.cn 2013-08-0118 */19 private static class ViewHolder {20  21     ImageView appIcon;22     TextView  appName;23     TextView  appInfo;24 }

 

關於ListView緩衝原理可見Android ListView緩衝機制。

 

4、其他點
(1) 用SurfaceView或TextureView代替普通View
SurfaceView或TextureView可以通過將繪圖操作移動到另一個單獨線程上提高效能。
普通View的繪製過程都是在主線程(UI線程)中完成,如果某些繪圖操作影響效能就不好最佳化了,這時我們可以考慮使用SurfaceView和TextureView,他們的繪圖操作發生在UI線程之外的另一個線程上。
因為SurfaceView在常規視圖系統之外,所以無法像常規試圖一樣移動、縮放或旋轉一個SurfaceView。TextureView是Android4.0引入的,除了與SurfaceView一樣在單獨線程繪製外,還可以像常規視圖一樣被改變。

 

(2) 使用RenderJavascript
RenderScript是Adnroid3.0引進的用來在Android上寫高效能代碼的一種語言,文法給予C語言的C99標準,他的結構是獨立的,所以不需要為不同的CPU或者GPU定製代碼代碼。

 

(3) 使用OpenGL繪圖
Android支援使用OpenGL API的高效能繪圖,這是Android可用的最進階的繪圖機制,在遊戲類對效能要求較高的應用中得到廣泛使用。
Android 4.3最大的改變,就是支援OpenGL ES 3.0。相比2.0,3.0有更多的緩衝區對象、增加了新的著色語言、增加多紋理支援等等,將為Android遊戲帶來更出色的視覺體驗。

 

(4) 盡量為所有解析度建立資源

減少不必要的硬體縮放,這會降低UI的繪製速度,可藉助Android asset studio

 

5、布局調優工具
(1) hierarchy viewer
hierarchy viewer可以方便的查看Activity的布局,各個View的屬性、measure、layout、draw的時間,如果耗時較多會用紅色標記,否則顯示綠色。
hierarchy viewer.bat位於<sdk>/tools/目錄下。使用可見:Using Hierarchy Viewer , 樣本圖如下:

 標註: hierarchy viewer目前在發布的商業版上處於安全考慮已經不可用:

To preserve security, Hierarchy Viewer can only connect to devices running a developer version of the Android system

  可參考http://lxfgrace.iteye.com/blog/1821869方案,just try

 

(2) layoutopt
layoutopt是一個可以提供layout及其層級最佳化提示的命令列,在sdk16以後已經被lint取代,在Windows->Show View->Other->Android->Lint Warnings查看lint最佳化提示,lint具體介紹可見Improving Your Code with lint。

 

來源:http://www.trinea.cn/android/layout-performance/

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.