Android ListView效能最佳化

來源:互聯網
上載者:User

標籤:android   效能最佳化   android listview   最佳化   

效能最佳化原理

ListView的設計目的就是可擴充和高效能。在實踐中,這意味著:

  1. 儘可能的少去執行Layout的Inflate
  2. 只渲染和布置那些在可視範圍內,或者即將出現在可視範圍內的Itemcode

第一條的原因非常簡單:Layout的Inflate是消耗資源巨大的代碼code。即使,Layout檔案已經被高效的解析程式轉換為了二進位代碼code。Infalte操作依舊需要徹底包含整個XML代碼樹,而且還要執行個體化相應的Viewcode。在Android 的源碼中,ListView通過View回收機制解決了這個問題code。這就意味著,開發人員可以非常簡單的通過可回收的View設定每個Item的內容code。而不用,為每一個Item都Inflate Layout 。

通過ListView的View回收機制,第二條也被實現了。她會將那些在可視範圍上面或者下面的View加入到回收池中。當在可視範圍內的View被移出可視範圍內時,其也會被添加到回收池中code。以這種方式,ListView只需佔用非常少的記憶體幾可以儲存可視範圍內的View和回收池中的View。即使,Adapter中有上百條Item,她也會啟動並執行非常高效。ListView的View回收機制以兩種不同的方式提供可回收的View——從上往下提供,和從下往上提供。採取何種方式,取決於滑動的方式code。下面這張圖展示了,當你下滑時ListView的View回收機制所做的工作。

讓我們把這個工作機制記在心中,現在開始讓我們把注意力轉移到最佳化ListView效能的小貼士上面去。如你上面所看到的那樣,當滑動ListView時,ListView動態提供可回收的View。因此,如何使你的Adapter的getView()變得儘可能輕巧成為了關鍵所在。所有的使ListView高效的小貼士的核心都是圍繞著如何讓getView()更加輕巧存在的。

要點一:使用可回收的View

每當ListView需要顯示一條新的Item的時候,她會回調你的Adapter的getView()方法。正如你你所知道的那樣,getView()方法有三個參數:Item的位置,convertView,Item的上級容器。

參數convertView實際上就是一個之前我們提到的可回收的View。當ListView要回收這個View的時候,她的資料就會被清空。因此,當convertView不為null的時候,你只需要將資料填充到裡面,而不用Inflate一個新的View。你的Adapter的getView()方法的代碼應該像下面這樣:

public View getView(int position, View convertView, ViewGroup parent) {    if (convertView == null) {        convertView = mInflater.inflate(R.layout.your_layout, null);    }    TextView text = (TextView) convertView.findViewById(R.id.text);    text.setText("Position " + position);    return convertView;}
要點二:使用ViewHolder

在一個已經Inflate的Layout中尋找View是Android開發中非常普遍的操作。這通常通過View的findViewById方法來實現。這個方法會遞迴整個View樹,以尋找那個與IDcode匹配的View。在靜態代碼中使用findViewById()還是非常棒的,但是,如你看到的那樣。當滑動ListView的時候,ListView會非常頻繁的回調Adapter的getView()方法。這就可能在不知不覺中影響ListView的滑動效能。尤其發生在你的Item的Layout非常的複雜的時候。

ViewHolder就是用來儲存那些在你的getView()方法中調用findViewById()方法得到的View。在實踐中,ViewHolder可以是一個非常輕巧的內部類。她儲存那些Item內部的View的直接引用。然後你可以在Inflate結束之後,將ViewHolderObject Storage Service在Item的tag當中。以這種方式,你只需要在第一次建立Item的時候調用findViewById就可以了。下面就是使用ViewHolder提高ListView的代碼:

public View getView(int position, View convertView, ViewGroup parent) {    ViewHolder holder;    if (convertView == null) {        convertView = mInflater.inflate(R.layout.your_layout, null);        holder = new ViewHolder();        holder.text = (TextView) convertView.findViewById(R.id.text);        convertView.setTag(holder);    } else {        holder = convertView.getTag();    }    holder.text.setText("Position " + position);    return convertView;}private static class ViewHolder {    public TextView text;}
要點三:非同步載入

在Android的App中,ListView使用像圖片這樣耗費資源的Item是非常普遍的。在你的Adapter中使用drawable資源是非常棒的,因為Android記憶體的代碼會緩衝這些資源code。但是,你可能會想要使用更加靈活的內容。比如來自本地裝置或者來自網路的縮圖或者圖片之類的。在這種情況下,你可能不想要直接的在你的Adapter的getView()中載入這些資源。因為你永遠不應該阻塞UI線程。同時這麼做也使得你的ListView滑動的更加平滑。

你所要做的事情就是讓那些需要IO操作或者耗費CPU資源的操作在一個額外的線程中運行。為了實現這個目的,你任然需要遵循ListView的View回收機制的規則。例如:當你的Adapter的getView()正在使用AsyncTask載入一張圖片。需要這張圖片的Item可能已經在圖片載入完成之前就已經被ListView的View回收機制回收了。因此你必須要知道,當你完成非同步載入的時候,對應的Item是否已經被回收了。

一個簡單的方法是,為你的AsyncTask提供一個識別資訊用以區別其對應的Item。這樣,當你的AsyncTask完成載入工作的時候,就可以判斷對應的Item還是不式最初的那個Item。事實上存在許多方式實現這個功能,下面的代碼只是其中最為簡單的一種:

public View getView(int position, View convertView,        ViewGroup parent) {    ViewHolder holder;    ...    holder.position = position;    new ThumbnailTask(position, holder)            .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null);    return convertView;}private static class ThumbnailTask extends AsyncTask {    private int mPosition;    private ViewHolder mHolder;    public ThumbnailTask(int position, ViewHolder holder) {        mPosition = position;        mHolder = holder;    }    @Override    protected Cursor doInBackground(Void... arg0) {        // Download bitmap here    }    @Override    protected void onPostExecute(Bitmap bitmap) {        if (mHolder.position == mPosition) {            mHolder.thumbnail.setImageBitmap(bitmap);        }    }}private static class ViewHolder {    public ImageView thumbnail;    public int position;}
要點四:互動意識

為ListView的每一個Item非同步載入大的資源是提升LitView的效能中很重要的一步。但是,如果當你滑動時,盲目的為每一個getView()開啟一個非同步任務是非常愚蠢的。你會浪費非常多的資源。因為大多數載入的資源都是無效的,因為滑動時Item的回收再利用是非常頻繁的。

我們需要給你的ListView加入互動意識。這樣她就不會在快速滑動時,為每一個Item都開啟一個非同步載入任務。要明白,當快速滑動時,你的非同步載入任務還沒有啟動,對應的Item就已經被回收了。每次當滑動停止的時候,或者將要停止的時候,就是你為每一個Item載入大的資源的時機。

我不會給出實現互動意識的代碼,因為這會是一大堆的代碼。但是由Romain Guy提供的 Shelves 應用提供了一個非常好的示範。在這個應用中,基本上只有當 GridView停止滑動時,非同步載入的任務才會被觸發。同時,你也可以在資料緩衝技術和意識互動技術之間做出一個平衡。當你使用資料緩衝技術的時候,你就需要在滑動的時候緩衝必要的資料。我想你是能理解我的意思的。

著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

Android ListView效能最佳化

聯繫我們

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