Android進階練習-改善布局效能

來源:互聯網
上載者:User

改進布局效能     布局是Android應用核心組成部分,它直接影響到使用者體驗,如果你沒有很好的實現,你實現的布局有可能導致應用記憶體吃緊,從而導致UI渲染變慢,Android SDK提供了一些工具來協助我們找出我們的布局中存在的效能問題,學完以下幾個知識點,你將有能力讓你的應用運行流暢,佔用很少的記憶體最佳化布局結構
     大家有個共同的誤區就是認為使用基本的布局結構能夠帶來最高效的布局效果,然而,任何一個添加進應用的控制項和布局都需要經過初始化、布繪和渲染,例如,使用嵌套的LinearLayout會導致過深的視圖層次,而且,嵌套幾個LinearLayout的話,尤其當你使用 layout_weight 屬性時是極度損耗效能的,因為LinearLayout的每一個子控制項都要測量兩次。這一點非常重要當你的布局需要反覆渲染顯示時,比如在你使用GirdView或ListView的時候。 詳細檢查你的布局
     Android SDK工具集包含了一個叫Hierarchy Viewer 的工具, 來協助分析當我們的應用在啟動並執行時候,布局的渲染效能,使用Hierarchy Viewer  工具能協助發現我們編寫的布局檔案的效能瓶頸

     Hierarchy Viewer 能夠讓我們通過選擇一個正在手機或模擬器上面啟動並執行進程來工作,然後顯示進程中正在後使用者互動的介面的控制項樹,每個塊的交通燈表示它的測量,布局和繪圖效能,協助你檢查出潛在的問題。

例如,圖-1顯示了在 ListView  中一項的布局,一張小圖片和兩行文本,這種需要多次渲染的布局最佳化的效能優勢將成倍增加。

Figure 1. Conceptual layout for an item in a ListView.

    hierarchyviewer 工具可以再<sdk>/tools/目錄下找到和使用,當你開啟它時,它會顯示當前你電腦已經串連的裝置和它們正在啟動並執行組件, 點擊 Load View Hierarchy 按鈕可以顯示所選組件的布局的層次
      Figure 2. Layout
hierarchy for the layout in figure 1, using nested instances ofLinearLayout.
         Figure 3. Clicking
a hierarchy node shows its performance times. 通過這個我們可以知道,每個層次,每個控制項測量、布局和繪製所花費的時間,從中可以找到我們需要最佳化的點

修複你的布局

            使用嵌套的布局檔案會使布局渲染效能下降,使用單一扁平的布局可能會改善效能,使布局淺寬比窄深更好,使用RelativeLayout 相對布局可以使布局層次更淺

Figure 4. Layout hierarchy for the layout in figure 1, using RelativeLayout.Now rendering a list item takes:
  • Measure: 0.598ms
  • Layout: 0.110ms
  • Draw: 2.146ms
 
使用RelativeLayout相對 來說會使效能改善點,但你想想當你需要顯示成千上萬個這樣的布局時,就可以提高很多效能了

 
這個時間差大部分是由於在LinearLayout中的設計使用layout_weight,這個會降慢測量的速度。我們應該適當的使用每一個布局,當你在使用LinearLayout時,需要想想是否真的有必要   在實際使用中發現 hierarchy 並不能工作於真機上,好像只能工作於模擬器上,不知道是不是我的機子問題 

使用Lint

      我們可以在布局檔案上使用Lint工具來尋找視圖可能存在的層次最佳化點,Lint已經替代了Layoutopt   工具並且具有很多更棒的功能,Lint的一些規則:
     
           使用複合畫板 - 在一個包含ImgeView和TextView的LinearLayout中使用複合畫板會更高效
       
         如果一個布局下面有子控制項但沒有兄弟控制項,且不是一個根布局,不是 ScrollView, 沒有背景,那麼可以移除它

           如果一個布局沒有包含子控制項,沒有背景,那麼它將不會在介面上顯示,我們可以移除它

           盡量使用平面布局 RelativeLayout or GridLayout

           如果以個FrameLayout 是根布局,且沒有背景,沒有填充,可以用merge標籤來取代它

     我們可以很方便的在eclipse中使用Lint,可以對一個項目、一個布局檔案使用Lint來分析那些地方需要最佳化,來獲得Lint給我們的建議

用<include/>複用布局
     儘管Android提供了多種多樣的小組件來構建小而可複用的互動式元素,但你可能需要更大的可複用的組件單元的來滿足一個特殊的布局需要,為了有效使用複用來完成布局,可以使用 <include/> and  <merge/> 標籤來使得另外一個布局單元嵌入到當前布局單元中
     重用布局是一項特別有用的手段來讓你有能力構建可重用複合型的布局,例如一個yes/no按鈕,含有描述資訊的自訂進度條。這個就意味著在你應用中的任何一個元素都可以給多個布局進行提取,分開管理,然後包含進各自的布局中。所以當你建立布局的私人UI組件來定製一個視圖時,其實你可以複用一個布局單元來更容易的做到這個。建立一個可以重用的布局單元
     當你知道你需要重用的布局時,你可以建立一個新的xml布局檔案來定義它,下面是一個需要被重用的布局單元,titlebar的布局檔案
< FrameLayout xmlns:android ="http://schemas.android.com/apk/res/android"    android:layout_width ="match_parent"    android:layout_height= "wrap_content"    android:background= "#FF000000" >    <ImageView android:layout_width= "wrap_content"               android:layout_height ="wrap_content"               android:src ="@drawable/titlebar"               android:contentDescription ="@string/app_name" /></ FrameLayout>
          如果想讓你的布局單元能在每一個布局中都能夠複用,根標籤應該恰當

使用<include/>標籤
     我們可以在布局中通過使用<include/>標籤來添加複用組件

< LinearLayout xmlns:android ="http://schemas.android.com/apk/res/android"    xmlns:tools= "http://schemas.android.com/tools"    android:layout_width= "match_parent"    android:layout_height= "match_parent"    android:paddingBottom= "@dimen/activity_vertical_margin"    android:paddingLeft= "@dimen/activity_horizontal_margin"    android:paddingRight= "@dimen/activity_horizontal_margin"    android:paddingTop= "@dimen/activity_vertical_margin"    tools:context= ".MainActivity"    android:orientation= "vertical" >         <include layout= "@layout/titlebar"/>    <TextView        android:layout_width ="wrap_content"        android:layout_height ="wrap_content"        android:text ="@string/hello_world" /></ LinearLayout>

 你也可以在當前布局中複寫重用組件的 android:layout_* 屬性
     <include        android:id ="@+id/news_title"         android:layout_width ="match_parent"         android:layout_height ="match_parent"        layout= "@layout/titlebar" />

使用<merge/>標籤
     <merge/>標籤可以協助你在你的視圖層次中消除多餘的視圖組,當一個布局包含另外一個布局時。當你的主布局是一個垂直結構的LinearLayout包含兩個連續的能被其它布局重用的視圖,被你放置在布局中的兩個可重用的視圖都需要各自的根視圖,使用另外一個LinearLayout來充當可重用視圖的根視圖時,會導致一個垂直結構的LinearLayout嵌套在另外一個垂直結構的LinearLayout中,嵌套的LinearLayout除了減慢你的UI渲染速度以外沒有任何的實際作用,為了避免這種情況的發生,我們可以使用<merge/>標籤來作為可重用布局組件的根視圖
<merge xmlns:android="http://schemas.android.com/apk/res/android">    <Button        android:layout_width="fill_parent"         android:layout_height="wrap_content"        android:text="@string/add"/>    <Button        android:layout_width="fill_parent"         android:layout_height="wrap_content"        android:text="@string/delete"/></merge>


     同樣是使用<include/>標籤來添加進布局中,這樣Android系統並不會理會<merge/>標籤,而是直接把兩個Button放置在布局中,避免了不必要的嵌套。另外需要注意的是<merge/>只可以作為布局的根節點,當需要包含其它布局組件的布局本身是以<merge/>為根節點的話,需要將被匯入的xml
layout置於viewGroup中,同時需要設定attachToRoot為True

使用ViewStub

     有些時候在我們的應用中可能會有一些極少使用到的複雜的視圖,可能是一個清單項目的詳細資料,一個進度列指示器,或者不用進行處理的資訊,當我們在使用視圖的時機去載入視圖,而不是應用已啟動就去載入,這樣應用能夠有效減少記憶體的使用和加快視圖的渲染速度定義一個ViewStub   
      ViewStub 是一個輕量級的View,它是一個沒有不佔布局位置,看不見的,佔用資源非常小的控制項

     <ViewStub        android:id ="@+id/stub_import"        android:layout_width ="fill_parent"        android:layout_height ="wrap_content"        android:layout_gravity ="bottom"        android:inflatedId ="@+id/item_details_import"        android:layout ="@layout/item_details" />

item_details.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" >       <TextView        android:id ="@+id/tv_stub_item_details"        android:layout_width= "wrap_content"        android:layout_height ="wrap_content"        android:text ="@string/item_details"               /></ LinearLayout>

     一旦visible/inflated,ViewStub就不在屬於布局結構中的一部分了,被它inflated的布局(item_details.xml)所替代了, ViewStub只能Inflate一次,之後ViewStub對象會被置為空白,ViewStub將變為不可用,不能夠再控制inflated的布局顯示與否了,ViewStub有一個缺點就是不支援所inflated的布局含有<merge/>標籤使用後台線程AsyncTask更新UI,使用ViewHolder
    使用AsyncTask
     UI的渲染載入都是運行在Android程式的主線程中,如果UI主線程阻塞的時間超過5s,應用程式將會無響應,給使用者帶來極不好的體驗,比如從伺服器上擷取一張圖片等耗時操作都有可能導致我們的應用無響應,這時我們可以使用後台線程非同步類AsyncTask來在後台載入資料更新UI

// Using an AsyncTask to load the slow images in a background threadnew AsyncTask<ViewHolder, Void, Bitmap>() {    private ViewHolder v;    @Override    protected Bitmap doInBackground(ViewHolder... params) {        v = params[0];        return mFakeImageLoader.getImage();    }    @Override    protected void onPostExecute(Bitmap result) {        super.onPostExecute(result);        if (v.position == position) {            // If this item hasn't been recycled already, hide the            // progress and set and show the image            v.progress.setVisibility(View.GONE);            v.icon.setVisibility(View.VISIBLE);            v.icon.setImageBitmap(result);        }    }}.execute(holder);

     Android3.0後,AynscTask有了一個新特性,可以在多核CPU上同時執行多個背景工作, 但要調用 executeOnExecutor()方法來替代execute()方法 使用ViewHolder

     當我們滑動ListView的時候會頻繁的調用findViewById()方法,這樣會降低我們程式的效能。即使Adapter能夠返回一個迴圈利用的已經渲染好的View,但依然需要去尋找View包含的元素並且需要更新它們,可以用"View Hodler"設計模式來解決反覆使用findViewById()所帶來的效能問題
     ViewHodler能夠儲存convetView中每一個元素,當下次渲染的時候只需要從ViewHodler中直接取就行了,而不用反覆的去findViewById()部分程式碼片段   
@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder;if (convertView == null) {convertView = mInflater.inflate(R.layout.applist_item, null);holder = new ViewHolder();holder.icon = (ImageView) convertView.findViewById(R.id.applist_item_iv_appicon);holder.name = (TextView) convertView.findViewById(R.id.applist_item_tv_appname);holder.pk = (TextView) convertView.findViewById(R.id.applist_item_tv_apppk);convertView.setTag(holder);} else {holder = (ViewHolder) convertView.getTag();}holder.icon.setImageDrawable((Drawable) datalist.get(position).get("icon"));holder.name.setText((String) datalist.get(position).get("name"));holder.pk.setText((String) datalist.get(position).get("pk"));return convertView;}static class ViewHolder {ImageView icon;TextView name;TextView pk;}




相關文章

聯繫我們

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