[Android]ListView性能優化之視圖緩存(續)

前言緩存

  在上一篇ListView性能優化之視圖緩存咱們討論了Google I/O中的優化方法,在各個論壇發帖後獲得了不錯的反饋,諸如:使用ViewHolder技術Tag的問題,利用HashMap自行存儲的方案等。這裏結合新浪微博中主界面的作法及測試數據與你們進一步探討。
 性能優化

 

聲明ide

  歡迎轉載,但請保留文章原始出處:)
 性能

    農民伯伯:http://over140.blog.51cto.com/  測試

文章優化

  [Android]ListView性能優化之視圖緩存 [本文的上篇]
 ui

  [Android]ListView性能優化之視圖緩存 [JavaEye討論帖]
 this

 

正文spa

  1、新浪微博code

    1.1  截圖

 

 

1.2  反編譯後相關代碼

      HomeListActivity

     public  View getView( int  paramInt, View paramView, ViewGroup paramViewGroup)
    {
      
int  i  =   -- paramInt;
      
int  j  =   - 1 ;
      
if  (i  ==  j);
      
for  (Object localObject1  =  HomeListActivity. this .getReloadView(); ; localObject1  =  HomeListActivity. this .getLoadMoreView())
      {
        label26: 
return  localObject1;
        
int  k  =  HomeListActivity. this .mList.size();
        
int  l  =  paramInt;
        
int  i1  =  k;
        
if  (l  !=  i1)
          
break ;
      }
      
boolean  bool1  =   true ;
      
boolean  bool2  =   null ;
      String str1;
      label110: Object localObject2;
      
if  (StaticInfo.mUser  ==   null )
      {
        List localList1 
=  HomeListActivity. this .mList;
        
int  i2  =  paramInt;
        str1 
=  ((MBlog)localList1.get(i2)).uid;
        List localList2 
=  HomeListActivity. this .mList;
        
int  i3  =  paramInt;
        String str2 
=  ((MBlog)localList2.get(i3)).uid;
        String str3 
=  str1;
        
if  ( ! str2.equals(str3))
          
break  label271;
        
int  i4  =   1 ;
        label156: 
if  (paramView  !=   null )
          
break  label277;
        HomeListActivity localHomeListActivity1 
=  HomeListActivity. this ;
        ListView localListView1 
=  HomeListActivity. this .mLvHome;
        List localList3 
=  HomeListActivity. this .mList;
        
int  i5  =  paramInt;
        MBlog localMBlog1 
=  (MBlog)localList3.get(i5);
        HomeListActivity localHomeListActivity2 
=  HomeListActivity. this ;
        
int  i6  =  paramInt;
        
boolean  bool4  =  localHomeListActivity2.isNewCommer(i6);
        
int  i7  =  HomeListActivity. this .mReadMode;
        localObject2 
=   new  MBlogListItemView(localHomeListActivity1, localListView1, localMBlog1, bool1, bool2, i4, bool4, i7);
      }
      
while  ( true )
      {
        localObject1 
=  localObject2;
        
break  label26:
        str1 
=  StaticInfo.mUser.uid;
        
break  label110:
        label271: 
boolean  bool3  =   null ;
        
break  label156:
        label277: localObject2 
=  paramView;
        
try
        {
          MainListItemView localMainListItemView 
=  (MainListItemView)localObject2;
          List localList4 
=  HomeListActivity. this .mList;
          
int  i8  =  paramInt;
          Object localObject3 
=  localList4.get(i8);
          HomeListActivity localHomeListActivity3 
=  HomeListActivity. this ;
          
int  i9  =  paramInt;
          
boolean  bool5  =  localHomeListActivity3.isNewCommer(i9);
          
int  i10  =  HomeListActivity. this .mReadMode;
          
boolean  bool6  =  bool1;
          
boolean  bool7  =  bool2;
          localMainListItemView.update(localObject3, bool6, bool7, bool5, i10);
        }
        
catch  (Exception localException)
        {
          HomeListActivity localHomeListActivity4 
=  HomeListActivity. this ;
          ListView localListView2 
=  HomeListActivity. this .mLvHome;
          List localList5 
=  HomeListActivity. this .mList;
          
int  i11  =  paramInt;
          MBlog localMBlog2 
=  (MBlog)localList5.get(i11);
          HomeListActivity localHomeListActivity5 
=  HomeListActivity. this ;
          
int  i12  =  paramInt;
          
boolean  bool8  =  localHomeListActivity5.isNewCommer(i12);
          
int  i13  =  HomeListActivity. this .mReadMode;
          localObject2 
=   new  MBlogListItemView(localHomeListActivity4, localListView2, localMBlog2, bool1, bool2, bool3, bool8, i13);
        }
      }
    }

        代碼說明:

          代碼流程已經比較混亂,可是這裏能看到並無直接的inflate,而是自定義了繼承自LinearLayout的MBlogListItemView。

      MBlogListItemView
   public  MBlogListItemView(Context paramContext, ListView paramListView, MBlog paramMBlog,  boolean  paramBoolean1,  boolean  paramBoolean2,  boolean  paramBoolean3,  boolean  paramBoolean4,  int  paramInt)
  {
    
super (paramContext);
    
this .context  =  paramContext;
    
this .parent  =  paramListView;
    
this .mBlog  =  paramMBlog;
    String str1 
=  paramContext.getCacheDir().getAbsolutePath();
    
this .mCacheDir  =  str1;
    String str2 
=  paramContext.getFilesDir().getAbsolutePath();
    
this .mFileDir  =  str2;
    ((LayoutInflater)paramContext.getSystemService(
" layout_inflater " )).inflate( 2130903061 this );
    TextView localTextView1 
=  (TextView)findViewById( 2131624016 );
    
this .mName  =  localTextView1;
    TextView localTextView2 
=  (TextView)findViewById( 2131624041 );
    
this .mDate  =  localTextView2;
    TextView localTextView3 
=  (TextView)findViewById( 2131624018 );
    
this .mContent  =  localTextView3;
    TextView localTextView4 
=  (TextView)findViewById( 2131624046 );
    
this .mSubContent  =  localTextView4;
    ImageView localImageView1 
=  (ImageView)findViewById( 2131624040 );
    
this .mIconV  =  localImageView1;
    ImageView localImageView2 
=  (ImageView)findViewById( 2131624042 );
    
this .mIconPic  =  localImageView2;
    ImageView localImageView3 
=  (ImageView)findViewById( 2131624044 );
    
this .mUploadPic1  =  localImageView3;
    ImageView localImageView4 
=  (ImageView)findViewById( 2131623979 );
    
this .mUploadPic2  =  localImageView4;
    TextView localTextView5 
=  (TextView)findViewById( 2131624047 );
    
this .tvForm  =  localTextView5;
    TextView localTextView6 
=  (TextView)findViewById( 2131623989 );
    
this .tvComment  =  localTextView6;
    
this .tvComment.setOnClickListener( this );
    TextView localTextView7 
=  (TextView)findViewById( 2131623988 );
    
this .tvRedirect  =  localTextView7;
    
this .tvRedirect.setOnClickListener( this );
    ImageView localImageView5 
=  (ImageView)findViewById( 2131624049 );
    
this .imComment  =  localImageView5;
    
this .imComment.setOnClickListener( this );
    ImageView localImageView6 
=  (ImageView)findViewById( 2131624048 );
    
this .imRedirect  =  localImageView6;
    
this .imRedirect.setOnClickListener( this );
    ImageView localImageView7 
=  (ImageView)findViewById( 2131624043 );
    
this .imGpsIcon  =  localImageView7;
    ImageView localImageView8 
=  (ImageView)findViewById( 2131624013 );
    
this .mPortrait  =  localImageView8;
    LinearLayout localLinearLayout 
=  (LinearLayout)findViewById( 2131624045 );
    
this .mSubLayout  =  localLinearLayout;
    
this .mReadMode  =  paramInt;
    MBlogListItemView localMBlogListItemView 
=   this ;
    MBlog localMBlog 
=  paramMBlog;
    
boolean  bool1  =  paramBoolean1;
    
boolean  bool2  =  paramBoolean2;
    
boolean  bool3  =  paramBoolean4;
    
int  i  =  paramInt;
    localMBlogListItemView.update(localMBlog, bool1, bool2, bool3, i);
    
this .mUploadPic1.setOnClickListener( this );
    
this .mUploadPic2.setOnClickListener( this );
  }

    代碼說明:

      a).  MBlogListItemView extends LinearLayout implements MainListItemView
 

      b).   inflate( 2130903061,this)這個數字表明R.layout.itemview。

 

  2、測試方案(方案五)

    按照新浪微博相似的作法進行測試。

    2.1  測試代碼

        @Override
        
public  View getView( int  position, View convertView, ViewGroup parent) {
            
//  開始計時
             long  startTime  =  System.nanoTime();

            TestItemLayout item;
            
if  (convertView  ==   null ) {
                item 
=   new  TestItemLayout(BaseAdapterActivity. this );
            } 
else
                item 
=  (TestItemLayout) convertView;
            item.icon1.setImageResource(R.drawable.icon);
            item.text1.setText(mData[position]);
            item.icon2.setImageResource(R.drawable.icon);
            item.text2.setText(mData[position]);

            
//  中止計時
             long  endTime  =  System.nanoTime();
            
//  計算耗時
             long  val  =  (endTime  -  startTime)  /   1000L ;
            Log.e(
" Test " " Position: "   +  position  +   " : "   +  val);
            
if  (count  <   100 ) {
                
if  (val  <   2000L ) {
                    sum 
+=  val;
                    count
++ ;
                }
            } 
else
                mTV.setText(String.valueOf(sum 
/   100L +   " : "   +  nullcount); //  顯示統計結果
             return  item;
        }

 

      TestItemLayout

public   class  TestItemLayout  extends  LinearLayout {

    
public  TextView text1;
    
public  ImageView icon1;
    
public  TextView text2;
    
public  ImageView icon2;

    
public  TestItemLayout(Context context) {
        
super (context);
        ((LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(
                R.layout.list_item_icon_text, 
this );
        icon1 
=  (ImageView) findViewById(R.id.icon1);
        text1 
=  (TextView) findViewById(R.id.text1);
        icon2 
=  (ImageView) findViewById(R.id.icon2);
        text2 
=  (TextView) findViewById(R.id.text2);
    }
}

    2.2  測試結果

次數

4個子元素

10個子元素

第一次

 347

460
 

第二次

310
 

477
 

第三次

 324

508
 

第四次

339
 

492
 

第五次

 341

465

 

  3、總結
 

    從測試結果來看與ViewHolder性能很是接近,不會出現tag圖片變小的問題(關於圖片變小的問題,有朋友說是TAG中的元素對大小和位置有記憶),也能有效的減小findViewById的執行次數,這裏建議徹底能夠取代ViewHolder。

    關於ListView內部Adapter的心得你們能夠看一下上文的總結4.1。

 

  4、考慮
 

    關於靜態內部類這裏不是很理解,是否能應用方案五還有待驗證。

 

 

  5、後期維護

           2011-4-29     來自Stony Wang關於Tag的解釋:
 

 

Stony Wang
tag的用途應該是仿照delphi的來的,設置一個關聯的數據。

簡單的說就是,你的UI控件有時候顯示的內容帶源於(綁定?)某個數據或者對象實例。
當你處理一些事件的時候,不推薦從UI上來從新獲取,而是從Tag裏面取出來。
 

舉一個例子是,有一個按鈕,一開始顯示"0",而後每按一次計數增1。
每次click的時候,
1 從btn.getText()再轉回Integer
2 從tag裏面把以前設好的Integer拿出來,加一,再settag?
 

若是以爲1和2區別不大的話,那麼若是顯示的內容不是"0",而是"已經點了0次"呢?
 

更新UI的時候,能夠將關聯的對象放到tag裏面,在處理相關觸發事件的時候,能夠方便的獲取原始的數據。
 

ListView的Tag用法也不算很錯,而是用的時候沒有注意設置的時候要注意「對稱」。
Tag自己能夠理解成放ViewHolder,那麼和ViewHolder的加速只不過是存放的位置不一樣,加速效果基本一致。
「對稱」我所指的內容是:
無論你要顯示的數據的邏輯是如何的,若是你設置了某個View的寬度,那麼在任何一種數據的填充UI邏輯裏面,不能夠有不設置這個View寬度的代碼路徑。
簡單的例子就是,我根據某個布爾值,若是是false的話,將ImageView設置爲View.INVISIBLE
if ( item.isHidden()){
  mImage.setVisibility(View.INVISIBLE);
 

}
 
這樣是錯誤的,由於若是這裏緩存的UI控件的狀態會被複用到其它item上,而這個item恰巧多是須要顯示的。
必須補上else語句
else{
  mImage.setVisibility(View.VISIBLE);
}
 
這個估計就是那位仁兄拖動圖片變小的緣由了。
最後說一下,ListView控件條目部分,一共產生的條目是屏幕能容納的數目+2(仍是+1?我記不清楚了),而後循環使用。

 

 

 

 

結束
 

  優化ListView不單單隻有對convertView的優化,還有許多這樣那樣的技巧,歡迎你們交流與分享 :)  

相關文章
相關標籤/搜索