在看了一些vogella的文章之后,发现关于android listview性能优化这一段很有意思,于是实践了一下,经过优化,性能确实提升不少!
先看看优化前和优化后的比较:
优化前的log截图:
![](http://static.oschina.net/uploads/space/2013/0323/232405_5Md7_172402.png)
优化后的log截图:
![](http://static.oschina.net/uploads/space/2013/0323/232442_2WhE_172402.png)
并且,在不停滚动ListView的过程中,优化之前会出现ANR现象,在AVD上特别容易复现:
![](http://static.oschina.net/uploads/space/2013/0323/232547_RF3j_172402.png)
然后,优化后显得很流畅,附上对于的log截图:
![](http://static.oschina.net/uploads/space/2013/0323/232657_gQDD_172402.png)
下面附上相关代码分析:
ListView中的每一个Item由一个ImageView 和一个TextView组成
Layout:
01 |
<? xml version = "1.0" encoding = "utf-8" ?> |
02 |
< LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android" |
03 |
android:layout_width = "fill_parent" |
04 |
android:layout_height = "fill_parent" |
05 |
android:orientation = "horizontal" > |
07 |
< ImageView android:id = "@+id/imageView" |
08 |
android:layout_width = "wrap_content" |
09 |
android:layout_height = "fill_parent" />" |
10 |
< TextView android:id = "@+id/textView" |
11 |
android:layout_width = "wrap_content" |
12 |
android:layout_height = "fill_parent" |
13 |
android:layout_marginLeft = "15dp" |
14 |
android:gravity = "center_vertical" /> |
Activity继承自ListActivity,我故意增加了Item,方便测试,效果更明显:
01 |
public class ListViewDemo extends ListActivity{ |
03 |
private final String[]
mItems = new String[]
{ "Android" , "iPhone" , |
04 |
"WindowsMobile" , "Blackberry" , "WebOS" , "Ubuntu" , "Windows7" , |
05 |
"Max
OS X" , "Linux" , "OS/2" , "Ubuntu" , "Windows7" , "Max
OS X" , |
06 |
"Linux" , "OS/2" , "Ubuntu" , "Windows7" , "Max
OS X" , "Linux" , "OS/2" , |
07 |
"Ubuntu" , "Windows7" , "Max
OS X" , "Linux" , "OS/2" , "Ubuntu" , |
08 |
"Windows7" , "Max
OS X" , "Linux" , "OS/2" , "Ubuntu" , "Windows7" , |
09 |
"Max
OS X" , "Linux" , "OS/2" , "Ubuntu" , "Windows7" , "Max
OS X" , |
10 |
"Linux" , "OS/2" , "Ubuntu" , "Windows7" , "Max
OS X" , "Linux" , "OS/2" , |
11 |
"Ubuntu" , "Windows7" , "Max
OS X" , "Linux" , "OS/2" , "Ubuntu" , |
12 |
"Windows7" , "Max
OS X" , "Linux" , "OS/2" , "Ubuntu" , "Windows7" , |
13 |
"Max
OS X" , "Linux" , "OS/2" , "Ubuntu" , "Windows7" , "Max
OS X" , |
14 |
"Linux" , "OS/2" , "Ubuntu" , "Windows7" , "Max
OS X" , "Linux" , "OS/2" , |
15 |
"Ubuntu" , "Windows7" , "Max
OS X" , "Linux" , "OS/2" , "Ubuntu" , |
16 |
"Windows7" , "Max
OS X" , "Linux" , "OS/2" , "Ubuntu" , "Windows7" , |
17 |
"Max
OS X" , "Linux" , "OS/2" , "Ubuntu" , "Windows7" , "Max
OS X" , |
18 |
"Linux" , "OS/2" , "Ubuntu" , "Windows7" , "Max
OS X" , "Linux" , "OS/2" , |
19 |
"Ubuntu" , "Windows7" , "Max
OS X" , "Linux" , "OS/2" , "Ubuntu" , |
20 |
"Windows7" , "Max
OS X" , "Linux" , "OS/2" , "Ubuntu" , "Windows7" , |
21 |
"Max
OS X" , "Linux" , "OS/2" , "Ubuntu" , "Windows7" , "Max
OS X" , |
22 |
"Linux" , "OS/2" , "Ubuntu" , "Windows7" , "Max
OS X" , "Linux" , "OS/2" , |
23 |
"Ubuntu" , "Windows7" , "Max
OS X" , "Linux" , "OS/2" , "Ubuntu" , |
24 |
"Windows7" , "Max
OS X" , "Linux" , "OS/2" , "Ubuntu" , "Windows7" , |
25 |
"Max
OS X" , "Linux" , "OS/2" , "Ubuntu" , "Windows7" , "Max
OS X" , |
26 |
"Linux" , "OS/2" , "Ubuntu" , "Windows7" , "Max
OS X" , "Linux" , "OS/2" , |
27 |
"Ubuntu" , "Windows7" , "Max
OS X" , "Linux" , "OS/2" , "Ubuntu" , |
28 |
"Windows7" , "Max
OS X" , "Linux" , "OS/2" , "Ubuntu" , "Windows7" , |
29 |
"Max
OS X" , "Linux" , "OS/2" , "Ubuntu" , "Windows7" , "Max
OS X" , |
30 |
"Linux" , "OS/2" , "Ubuntu" , "Windows7" , "Max
OS X" , "Linux" , "OS/2" }; |
32 |
public void onCreate(Bundle
savedInstanceState) { |
33 |
super .onCreate(savedInstanceState); |
34 |
ListViewArrayAdapter
adapter = new ListViewArrayAdapter( this ,
mItems); |
35 |
getListView().setAdapter(adapter); |
然后custom Adapter,优化之前的adapter:
02 |
public View
getView( int position,
View convertView, ViewGroup parent) { |
03 |
long start
= System.currentTimeMillis(); |
04 |
LayoutInflater
inflater = (LayoutInflater) mContext.getLayoutInflater(); |
05 |
View
rowView = inflater.inflate(mViewResourceId, parent, false ); |
06 |
TextView
textView = (TextView) rowView |
07 |
.findViewById(mTextViewResourceId); |
08 |
ImageView
imageView = (ImageView) rowView |
09 |
.findViewById(mImageViewResourceId); |
10 |
textView.setText(mNames[position]); |
11 |
String
s = mNames[position]; |
12 |
if (s.startsWith( "Windows7" )
|| s.startsWith( "iPhone" ))
{ |
13 |
imageView.setImageResource(R.drawable.no); |
15 |
imageView.setImageResource(R.drawable.yes); |
18 |
Log.v( "jerikc" , "cost
time = " +
(System.currentTimeMillis() - start)); |
优化之后的Adapter:
01 |
public class ListViewArrayAdapter extends ArrayAdapter<String>{ |
03 |
private final Activity
mContext; |
04 |
private final String[]
mNames; |
05 |
private final static int mViewResourceId
= R.layout.text_image_row_layout; |
06 |
private final static int mTextViewResourceId
= R.id.textView; |
07 |
private final static int mImageViewResourceId
= R.id.imageView; |
08 |
static class ViewHolder
{ |
10 |
public ImageView
image; |
13 |
public ListViewArrayAdapter(Activity
context, String[] names) { |
14 |
super (context,
mViewResourceId, names); |
15 |
this .mContext
= context; |
20 |
public View
getView( int position,
View convertView, ViewGroup parent) { |
21 |
long start
= System.currentTimeMillis(); |
22 |
View
rowView = convertView; |
23 |
if (rowView
== null )
{ |
24 |
LayoutInflater
inflater = mContext.getLayoutInflater(); |
25 |
rowView
= inflater.inflate(mViewResourceId, null ); |
26 |
ViewHolder
viewHolder = new ViewHolder(); |
27 |
viewHolder.text
= (TextView) rowView.findViewById(mTextViewResourceId); |
28 |
viewHolder.image
= (ImageView) rowView.findViewById(mImageViewResourceId); |
29 |
rowView.setTag(viewHolder); |
32 |
ViewHolder
holder = (ViewHolder) rowView.getTag(); |
33 |
String
s = mNames[position]; |
34 |
holder.text.setText(s); |
35 |
if (s.startsWith( "Windows7" )
|| s.startsWith( "iPhone" ))
{ |
36 |
holder.image.setImageResource(R.drawable.no); |
38 |
holder.image.setImageResource(R.drawable.yes); |
41 |
Log.v( "jerikc" , "cost
time = " +
(System.currentTimeMillis() - start)); |
优化的大致思想就是:优化之前,每次加载item的时候,都要加载一下布局文件,然后生成一个新的row View对象,然后通过View找到对应的ImageView和TextView,正如我们所知道的那样,加载布局文件时很耗时的,特别是在操作比较频繁情况下,这是不可忍受的,所以会导致ANR现象。
因此,我们可以重复利用已不可见的row View对象。Android中,当它决定让row View对象不可见的时候,它允许通过getView方法中的convertView参数来重复利用刚刚不可见的row View对象。
在优化的过程中,第一次加载的时候,我们需要把相关的数据保存起来,而View有一个方法setTag,该方法可用来保存一些数据结构。我们一个row View对象是由ImageView和TextView空间组成的,因此定义一个ViewHolder来保存ImageView和TextView对象。在重复利用的过程中,只需简单修改它们的值,而不用再次findViewById。
关于findViewById耗时的分析,可参考:
http://www.cnblogs.com/over140/archive/2011/03/23/1991100.html