1. 程式人生 > >Android開發之ListView Adapter優化

Android開發之ListView Adapter優化

   與所謂“戰鬥民族”一團隊合作開發一專案,最後收尾階段開始優化,app真特麼慢,先重adapter開始

Android在UI優化方面可以從以下五個方面入手:

  ◆Adapter優化

  ◆背景和圖片優化

  ◆繪圖優化

  ◆檢視和佈局優化

  ◆記憶體分配優化

  Adapter優化

  什麼是Adapter?

  Adapter在Android中佔據一個重要的角色,它是資料和UI(View)之間一個重要的紐帶。在常見的View(ListView,GridView)等地方都需要用到Adapter。如圖1直觀的表達了Data、Adapter、View三者的關係。

Adapter、資料、UI三者關係
圖1 Adapter、資料、UI三者關係

  一、Android中Adapter

Android-Adapter
圖2:Android中Adapter型別層級圖

  由圖2我們可以看到在Android中與Adapter有關的所有介面、類的完整層級圖。在我們使用過程中可以根據自己的需求實現介面或者繼承類進行一定的擴充套件。比較常用的有 BaseAdapter,ArrayAdapter,SimpleCursorAdapter等。

  BaseAdapter是一個抽象類,繼承它需要實現較多的方法,所以也就具有較高的靈活性;

  ArrayAdapter支援泛型操作,通常需要實現getView方法,特殊情況下(結合資料row id),為了讓ui事件相應處理方便點最好重寫getItemId;

  SimpleCursorAdapter可以適用於簡單的純文字型ListView,它需要Cursor的欄位和UI的id對應起來。如需要實現更復雜的UI也可以重寫其他方法。

  二、一個繼承BaseAdapter的類的程式碼段

1. 1: /**
2. 2: * 歌曲列表介面卡
3. 3: *
4. 4: *
@version 2010-11-24 下午05:13:33
5. 5: *
@author Hal
6. 6:
*/7. 7: publicclass AudioListAdapter extends BaseAdapter {
8. 8:
9. 9: private Context mContext;
10. 10:
11. 11: // 歌曲集合 12. 12: private ArrayList<Audio> mAudios;
13. 13:
14. 14: public AudioListAdapter(Context mContext, ArrayList<Audio> mAudios) {
15. 15: this.mContext = mContext;
16. 16: this.mAudios = mAudios;
17. 17: }
18. 18:
19. 19: @Override
20. 20: publicint getCount() {
21. 21: return mAudios !=null? mAudios.size() : 0;
22. 22: }
23. 23:
24. 24: @Override
25. 25: public Object getItem(int position) {
26. 26: if ((mAudios !=null&& mAudios.size() >0) && (position >=0&& position < mAudios.size())) {
27. 27: return mAudios.get(position);
28. 28: }
29. 29: returnnull;
30. 30: }
31. 31:
32. 32: /**
33. 33: * 如果集合中的物件資料來自資料庫,建議此方法返回該物件在資料庫中的ID
34. 34:
*/35. 35: @Override
36. 36: publiclong getItemId(int position) {
37. 37: if ((mAudios !=null&& mAudios.size() >0) && (position >=0&& position < mAudios.size())) {
38. 38: return mAudios.get(position).getId();
39. 39: }
40. 40: return position;
41. 41: }
42. 42:
43. 43: @Override
44. 44: public View getView(int position, View convertView, ViewGroup parent) {
45. 45: //TODO 返回自定的View 46. 46: }

  Adapter與View的連線主要依靠getView這個方法返回我們需要的自定義view。ListView是Android app中一個最最最常用的控制元件了,所以如何讓ListView流暢執行,獲取良好的使用者體驗是非常重要的。對ListView優化就是對Adapter中的getView方法進行優化。09年的Google IO大會給出的優化建議如下:

  Adapter優化示例程式碼:

1. @Override
2. public View getView(int position, View convertView, ViewGroup parent) {
3. Log.d("MyAdapter", "Position:"+ position +"---"4. + String.valueOf(System.currentTimeMillis()));
5. ViewHolder holder;
6. if (convertView ==null) {
7. final LayoutInflater inflater = (LayoutInflater) mContext
8. .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
9. convertView = inflater.inflate(R.layout.list_item_icon_text, ull);
10. holder =new ViewHolder();
11. holder.icon = (ImageView) convertView.findViewById(R.id.icon);
12. holder.text = (TextView) convertView.findViewById(R.id.text);
13. convertView.setTag(holder);
14. } else {
15. holder = (ViewHolder) convertView.getTag();
16. }
17. holder.icon.setImageResource(R.drawable.icon);
18. holder.text.setText(mData[position]);
19. return convertView;
20. }
21.
22. staticclass ViewHolder {
23. ImageView icon;
24.
25. TextView text;

  以上是Google io大會上給出的優化建議,經過嘗試ListView確實流暢了許多。

1. @Override
2. public View getView(int position, View convertView, ViewGroup parent) {
3. Log.d("MyAdapter", "Position:"+ position +"---"4. + String.valueOf(System.currentTimeMillis()));
5. final LayoutInflater inflater = (LayoutInflater) mContext
6. .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
7. View v = inflater.inflate(R.layout.list_item_icon_text, null);
8. ((ImageView) v.findViewById(R.id.icon)).setImageResource(R.drawable.icon);
9. ((TextView) v.findViewById(R.id.text)).setText(mData[position]);
10. return v;
11. }

  以上是不建議的做法!!

  不過我們還是要懷疑一下,SO,我們還是來測試對比一下。

  測試說明:

  大家可以看到在getView的時候我們通過log打印出position和當前系統時間。我們通過初始化1000條資料到Adapter顯示到ListView,然後滾動到底部,計算出position=0和position=999時的時間間隔。

  測試機子:HTC Magic

  測試實錄:開啟測序,讓ListView一直滾動底部。

device

  測試結果:

  兩種情況在操作過程中體驗明顯不同,在優化的情況下流暢很多很多!

  1、優化建議測試結果

1. 12-0510:44:46.039: DEBUG/MyAdapter(13929): Position:0---12915170860432. 12-0510:44:46.069: DEBUG/MyAdapter(13929): Position:1---12915170860723. 12-0510:44:46.079: DEBUG/MyAdapter(13929): Position:2---12915170860854.
5. ……
6.
7. 12-0510:45:04.109: DEBUG/MyAdapter(13929): Position:997---12915171041128. 12-0510:45:04.129: DEBUG/MyAdapter(13929): Position:998---12915171041359. 12-0510:45:04.149: DEBUG/MyAdapter(13929): Position:999---129151710415410.
11. 耗時:1796712.

  2、沒優化的測試結果

1. 12-0510:51:42.569: DEBUG/MyAdapter(14131): Position:0---12915175025732. 12-0510:51:42.589: DEBUG/MyAdapter(14131): Position:1---12915175025903. 12-0510:51:42.609: DEBUG/MyAdapter(14131): Position:2---12915175026174.
5. ……
6.
7. 12-0510:52:07.079: DEBUG/MyAdapter(14131): Position:998---12915175270828. 12-0510:52:07.099: DEBUG/MyAdapter(14131): Position:999---12915175271089.
10. 耗時:2453511.

  在1000條記錄的情況下就有如此差距,一旦資料nW+,ListView的Item佈局更加複雜的時候,優化的作用就更加突出了!

------------------------------

ListView作為Android開發中使用頻率最高的一個控制元件,保證ListView的流暢執行,對使用者體驗的提高至關重要。Adapter是ListView和資料來源之間的中間人,當每條資料進入可見區時,Adapter 的 getView() 會被呼叫,返回代表具體資料的檢視,在成百上千條資料觸控滾動時頻繁呼叫,因此如何優化Adapter是提高ListView效能的關鍵。

1. 使用ViewHolder模式,重複利用convertView,減少頻繁查詢

在2009年 Google IO開發者大會中已做說明,看一下使用不同實現方式之間的差距:

Adapter 顯示每條資料的 XML 佈局檔案如下:

<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="horizontal"> 
    <ImageView android:id="@+id/icon" 
        android:layout_width="48dip" 
        android:layout_height="48dip" /> 
    <TextView android:id="@+id/text" 
        android:layout_gravity="center_vertical" 
        android:layout_width="0dip" 
        android:layout_weight="1.0" 
        android:layout_height="wrap_content" /> 
</LinearLayout>

1. 最慢最不實用的方式

public View getView(int position, View convertView, ViewGroup parent) {
	View item = mInflater.inflate(R.layout.list_item_icon_text, null);

	((TextView) item.findViewById(R.id.text)).setText(DATA[position]);
	((ImageView) item.findViewById(R.id.icon)).setImageBitmap(
			(position & 1) == 1 ? mIcon1 : mIcon2);

	return item;
}

2. 使用 convertView 回收檢視, 效率提高 200%

public View getView(int position, View convertView, ViewGroup parent) { 
     if (convertView == null) { 
          convertView = mInflater.inflate(R.layout.item, null); 
     } 

     ((TextView) convertView.findViewById(R.id.text)).setText(DATA[position]); 
     ((ImageView) convertView.findViewById(R.id.icon)).setImageBitmap( 
               (position & 1) == 1 ? mIcon1 : mIcon2); 

     return convertView; 
}

3. 使用 ViewHolder 模式, 效率再提高 50%

static class ViewHolder { 
	TextView text; 
	ImageView icon; 
} 
public View getView(int pos, View convertView, ViewGroup parent){ 
    ViewHolder holder; 
    if (convertView == null) { 
        convertView = mInflater.inflate(R.layout.list_item, null); 
        holder = new ViewHolder();  
        holder.text = (TextView) convertView.findViewById(R.id.text)); 
        holder.icon = (ImageView) convertView.findViewButId(R.id.icon)); 
        convertView.setTag(holder); 
    } else { 
        holder = (ViewHolder) convertView.getTag(); 
    }  
    holder.text.setText(DATA[pos]); 
    holder.icon.setImageBitmap((pos & 1) == 1 ? mIcon1 : mIcon2); 
    return convertView; 
} 

更新率比較如下圖:

QQ截圖20130819151124

2. 使用工作執行緒載入資料,減輕UI主執行緒負擔,使UI主執行緒只專注於UI繪製

// Using an AsyncTask to load the slow images in a background thread
new 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);

3. 優化item佈局,儘量優化子view佈局不被過渡重繪,每一點子view的優化都能提高整體的效能

優化佈局層次結構

一個普遍的誤解就是,使用基本的佈局結構會產生高效的佈局效能。然而每一個新增到應用的控制元件和佈局,都需要初始化,佈局位置和繪製。比如,使用一個巢狀的LinearLayout會導致過深的佈局層次結構。此外,巢狀多個使用layout_weight屬性的LinearLayout例項會花費更大的代價,因為每一個子佈局都要測量兩次。當某個佈局被頻繁渲染時,比如它在ListView或GridView中使用,就顯得尤為重要。

在這節課中,將學會使用Hierachy Viewer和Layoutopt工具對佈局結構進行檢測和優化。

檢測你的佈局

在Android SDK tools中包含一個叫做HierchyViewer工具,它可以在你執行應用時候幫助你分析你的佈局效能。通過它你可以發現你的佈局中效能比較差的那些地方。

HierchyViewer需要你選擇一個已連結的裝置或者模擬器中的一個執行的執行緒,顯示出佈局的樹結構。每個塊上的紅綠燈代表它的測量,佈局,以及繪圖效能,幫助你找出潛在的問題。

比如,圖1顯示了一個用於ListView中的Item的佈局。這個佈局的左邊顯示了一幅圖片,兩個疊在一起的文字item放在右邊。那些被重複載入的佈局在優化時候顯得有為重要。

圖1. 一個ListView內item的概念設計

hierchyviewer 工具可以在<sdk>/tools/中找到。當開啟給工具後,就會顯示可用的裝置列表一個這些裝置中執行的部分。點選“Load View Hierchy”選項檢視被選中部分的佈局層次圖。比如,圖2顯示了圖1中佈局結構圖。

圖2. 圖1的佈局層次結構圖,使用內嵌的LinearLayout例項佈局。

 

圖3. 點選層次圖中一個節點,顯示它的執行時間

圖2中,你可以看到一個3層的佈局結構圖,並且在佈局text的items裡面有一些問題。點選這些items顯示程序中每個階段所花費的時間。它顯示的很清楚,哪些items在測試,佈局中花費時間最長,哪些地方需要花費時間去優化。

使用該佈局載入所有item所花費的時間如下:

  • Measure: 0.977ms

  • Layout: 0.167ms

  • Draw: 2.717ms

修改佈局

因為上述佈局效能較低的原因主要是由一個內嵌的LinearLayout所引起,將該佈局使用淺而廣的扁平化結構代替深而窄的樹形結構化設計,從而提高效能。在這些佈局中,將RelativeLayout作為一個根節點,這樣,你將會看到該佈局變為一個2層的結構,修改後的佈局如下:

圖4. 使用RelativeLayout的圖1的佈局。

修改後載入item所花費的時間:

  • Measure: 0.598ms

  • Layout: 0.110ms

  • Draw: 2.146ms

雖然看起來提高度很小,但是這佈局提高是被重複操作的,因此,這個佈局是在listview中的每一個item裡面。

更多情況的一個時間差異,是在使用了layout_weight屬性的LinearLayout設計裡面,這樣的設計會降低測量的速度。這只是一個示例說明每個佈局是否被適當的使用,在使用layout weight屬性時候,你應該謹慎考慮是否必要。

使用Lint

這是一個好習慣,在你的佈局檔案內執行Lint工具,尋找那些可能要優化的佈局結構。Lint工具代替Layoutopt工具,並且有更大的功能。如下是Lint的一些示例:

  • 使用複雜的圖片:在LineraLayout佈局中包含一個ImageView和一個TextView,可以使用一個複雜的drawable代替,效能會更好。
  • 合併根框架:假如一個FrameLayout作為一個佈局的根檢視,不提供背景或者填充,它可以被一個帶有<merge/>標誌的佈局代替。
  • 無用的樹葉:對於一個扁平結構中一個佈局沒有孩子,沒有背景,可以被刪掉。
  • 無用的父類:一個佈局不是ScrollView或者不是一個根佈局,也沒有背景,只有一個孩子節點,可以被刪掉,孩子節點直接放入到這個扁平的父類裡面。
  • 深度佈局:佈局若有太多內嵌,則效能很差。考慮使用RelativeLayout 以及GridLayout等扁平化佈局代替。預設佈局最大深度是10.

使用Lint另一個好處是,它被內嵌到ADT16+.當你在匯入apk,編輯或者儲存一個xml檔案,Lint都會自動執行。點選Eclipse工具欄中Lint按鈕,會人為強制執行Lint.

在Eclipse內使用Lint,它能自動修復一些問題,為問題提供修改建議,直接掉轉到問題程式碼位置。如果你不是用Eclipse開發,也可以使用命令列啟動Lint。更多資訊請參照tools.android.com.

 參考:

http://kb.cnblogs.com/page/84589/

http://developer.android.com/training/improving-layouts/optimizing-layout.html

http://www.cnblogs.com/purediy/p/3267913.html

相關推薦

Android開發ListView Adapter優化

   與所謂“戰鬥民族”一團隊合作開發一專案,最後收尾階段開始優化,app真特麼慢,先重adapter開始 Android在UI優化方面可以從以下五個方面入手:   ◆Adapter優化   ◆背景和圖片優化   ◆繪圖優化   ◆檢視和佈局優化   ◆記憶體

android 開發 ListViewAdapter 應用實踐

在開發android中,ListView 的應用顯得非常頻繁,只要需要顯示列表展示的應用,可以說是必不可少,下面是記錄開發中應用到ListView與Adapter 使用的例項: ListView 所在頁面中的佈局(listview_item.xml): <?xml version="1.0"

Android開發ListView 優化快取優化

通過平時對ListView的使用,目前我把ListView的優化分為以下幾個方面: 快取優化、資料優化、其他方面優化 0.未優化簡單程式碼 <span style="font-size:14

Android開發listview優化+圖片非同步載入+避免圖片顯示閃爍(修改版)

小鹿路過此地。。心熱之下寫寫Android開發中的listview運用(閒的沒事幹,改動了一點程式碼,但實現功能不變) 小鹿學Android開發已有兩年多了,總算有一點點小收穫,雖算不上大牛大神級人物,但有些東西可以和一家一起分享一起交流一起學習一起進步...。還有,望檢視

Android開發ListView與RecyclerView的對比”

在Android開發最火熱的時候ListView是最長使用的一種展示多item的控制元件,而在2018年的現在已經很少有人用ListView了,使用最多當數RecyclerView了。 下面總結一下兩者的區別: 兩者的用法區別 佈局效果 對空資料的處理 HeaderV

Android開發UI佈局優化全面總結

Android開發最常見的問題之一是螢幕碎片化太嚴重,所以我們在寫佈局的時候儘量不能適應硬編碼去佈局。 佈局優化在開發過程中起到至關重要的作用。 1.合用weightSum屬性和layout_weig

Android開發ListView

在安卓開發中,List View很常用,通過List View使應用程式可以在一個頁面顯示多個條目資訊,並且每個條目資訊佈局是一樣的。 以下是一個例項: main_activity.xml <?xml version="1.0" encoding

Android開發listView使用(手機應用列表顯示)

public class MainActivity extends Activity implements OnItemLongClickListener { private ListView lv_main; private List<AppInfo> data; private App

Android簡單開發 通用Adapter ViewHolder

chap int mage getitem fail abs earch this get 我們尋常使用Adapter的方式 public class BusbaseSearchApadter extends SimpleBaseApadter { priva

android開發merge結合include優化布局

ted com match clas you title example ews 文件的 merge結合include優化android布局,效果不知道。個人感覺使用上也有非常大的局限。只是還是了解一下。記錄下來。 布局文件都要有根節點,但androi

Android開發漫漫長途 XVI——ListView與RecyclerView項目實戰

列表 系列 緩存 廣泛 原理 前言 評論 request 功能點 該文章是一個系列文章,是本人在Android開發的漫漫長途上的一點感想和記錄,我會盡量按照先易後難的順序進行編寫該系列。該系列引用了《Android開發藝術探索》以及《深入理解Android 卷Ⅰ,Ⅱ,Ⅲ》中

Android開發多Fragment切換優化

rst 心得 getc format ref pri 就是 rip eight 問題分析 一直在簡書裏看別人的技術貼,今天我也來寫點自己的心得!最近在寫一個項目用到大量的Fragment後的總結! 我想剛剛接觸安卓的同學或許會這麽寫: FragmentManager

android開發listview繫結資料的三種方式

第一種,使用simple adapter simpleadapter 是最簡單的一種方式,但是其資料來源必須是map型別。 1.生成 SimpleAdapter()物件 2.設定資料來源 3.設定每個item的佈局 4.設定SimpleAdapter(Context con

Android開發ScrollView中巢狀ListView的解決方案

import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.widget.BaseAdapter;

android開發橫向滾動/豎向滾動的ListView(固定列頭)

由於專案需要,我們需要一個可以橫向滾動的,又可以豎向滾動的 表格。而且又要考慮大資料量(行)的展示檢視。經過幾天的研究終於搞定,做了一個演示。貼圖如下:         好吧。讓我們看思路是什麼樣的:   1. 上下滾動直接使用 listView來實現。 2

Android開發逐幀動畫優化

Android上如果使用逐幀動畫的話,可以很方便地使用AnimationDrawable,無論是先宣告xml還是直接程式碼裡設定,都是幾分鐘的事,但使用AnimationDrawable有一個致命的弱點,那就是需要一次性載入所有圖片到記憶體,萬一幀數多了或者每張圖片都比較大

Android開發getX,getRawX,getWidth,getTranslationX等的區別

save string hlist getwidth sta 是我 touch 項目 寬度 轉載請註明出處:http://blog.csdn.net/dmk877/article/details/51550031 好久沒寫博客了,最近工作確實挺忙的,剛剛結束了一個

Android 開發Windows環境下Android Studio安裝和使用教程(圖文詳細步驟)

9.png 虛擬機 jdk版本 編寫 clip 開發平臺 集成開發 arc 電腦安裝 鑒於谷歌最新推出的Android Studio備受開發者的推崇,所以也跟著體驗一下。 一、介紹Android Studio Android Studio 是一個Android

Android開發AudioManager(音頻管理器)具體解釋

應該 數量 service eth out 開發 要求 type 路由 AudioManager簡單介紹: AudioManager類提供了訪問音量和振鈴器mode控制。使用Context.getSystemService(Context.AUDIO_SERVICE)

【入門篇】ANDROID開發BUG專講

world 自然 執行 類型 效率 str 積累 全部 href 話說諸葛亮是一個優秀的程序員,每個錦囊都是應對不同的case而編寫的。可是優秀的程序員也敵只是更優秀的bug。六出祈山。七進中原,鞠躬盡瘁,死而後已的諸葛亮僅僅由於有一