前言
ListView是Android中最常用的控件,通过适配器来进行数据适配然后显示出来,而其性能是个很值得研究的话题。本文与你一起探讨Google I/O提供的优化Adapter方案,欢迎大家交流。
正文
一、准备
1.1 了解关于Google IO大会关于Adapter的优化,参考以下文章:
Android开发之ListView 适配器(Adapter)优化
Android开发——09Google I/O之让Android UI性能更高效(1)
PDF下载:Google IO.pdf
1.2 准备测试代码:
Activity
privateTestAdaptermAdapter;
privateString[]mArrData;
privateTextViewmTV;
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mTV=(TextView)findViewById(R.id.tvShow);
mArrData=newString[1000];
for(inti=0;i<1000;i++){
mArrData[i]="GoogleIOAdapter"
+ i;
}
mAdapter=newTestAdapter(this,mArrData);
((ListView)findViewById(android.R.id.list)).setAdapter(mAdapter);
}
代码说明:模拟一千条数据,TestAdapter继承自BaseAdapter,main.xml见文章末尾下载。
二、测试
测试方法:手动滑动ListView至position至50然后往回滑动,充分利用convertView不等于null的代码段。
2.1 方案一
按照Google I/O介绍的第二种方案,把item子元素分别改为4个和10个,这样效果更佳明显。
2.1.1 测试代码
privateintcount=0;
privatelongsum=0L;
@Override
publicViewgetView(intposition,ViewconvertView,ViewGroupparent){
//开始计时
longstartTime=System.nanoTime();
if(convertView==null){
convertView=mInflater.inflate(R.layout.list_item_icon_text,
null);
}
((ImageView)convertView.findViewById(R.id.icon1)).setImageResource(R.drawable.icon);
((TextView)convertView.findViewById(R.id.text1)).setText(mData[position]);
((ImageView)convertView.findViewById(R.id.icon2)).setImageResource(R.drawable.icon);
((TextView)convertView.findViewById(R.id.text2)).setText(mData[position]);
//停止计时
longendTime=System.nanoTime();
//计算耗时
longval=(endTime-startTime)/1000L;
Log.e("Test","Position:"+position+":"+val);
if(count<100){
if(val<1000L){
sum+=val;
count++;
}
}else
mTV.setText(String.valueOf(sum/100L));//显示统计结果
returnconvertView;
}
2.1.2 测试结果(微秒除以1000,见代码)
次数
|
4个子元素
|
10个子元素
|
第一次
|
366
|
723
|
第二次
|
356
|
689
|
第三次
|
371
|
692
|
第四次
|
356
|
696
|
第五次
|
371
|
662
|
2.2 方案二
按照Google I/O介绍的第三种方案,是把item子元素分别改为4个和10个。
2.2.1 测试代码
privateintcount=0;
privatelongsum=0L;
@Override
publicViewgetView(intposition,ViewconvertView,ViewGroupparent){
//开始计时
longstartTime=System.nanoTime();
ViewHolderholder;
if(convertView==null){
convertView=mInflater.inflate(R.layout.list_item_icon_text,
null);
holder=newViewHolder();
holder.icon1=(ImageView)convertView.findViewById(R.id.icon1);
holder.text1=(TextView)convertView.findViewById(R.id.text1);
holder.icon2=(ImageView)convertView.findViewById(R.id.icon2);
holder.text2=(TextView)convertView.findViewById(R.id.text2);
convertView.setTag(holder);
}
else{
holder=(ViewHolder)convertView.getTag();
}
holder.icon1.setImageResource(R.drawable.icon);
holder.text1.setText(mData[position]);
holder.icon2.setImageResource(R.drawable.icon);
holder.text2.setText(mData[position]);
//停止计时
longendTime=System.nanoTime();
//计算耗时
longval=(endTime-startTime)/1000L;
Log.e("Test","Position:"+position+":"+val);
if(count<100){
if(val<1000L){
sum+=val;
count++;
}
}else
mTV.setText(String.valueOf(sum/100L));//显示统计结果
returnconvertView;
}
}
staticclassViewHolder{
TextViewtext1;
ImageViewicon1;
TextViewtext2;
ImageViewicon2;
}
2.2.2 测试结果(微秒除以1000,见代码)
次数
|
4个子元素
|
10个子元素
|
第一次
|
311
|
417
|
第二次
|
291
|
441
|
第三次
|
302
|
462
|
第四次
|
286
|
444
|
第五次
|
299
|
436
|
2.3 方案三
此方案为“Henry Hu”提示,API Level 4以上提供,这里顺带测试了一下不使用静态内部类情况下性能。
2.3.1 测试代码
@Override
publicViewgetView(intposition,ViewconvertView,ViewGroupparent){
//开始计时
longstartTime=System.nanoTime();
if(convertView==null){
convertView=mInflater.inflate(R.layout.list_item_icon_text,null);
convertView.setTag(R.id.icon1,convertView.findViewById(R.id.icon1));
convertView.setTag(R.id.text1,convertView.findViewById(R.id.text1));
convertView.setTag(R.id.icon2,convertView.findViewById(R.id.icon2));
convertView.setTag(R.id.text2,convertView.findViewById(R.id.text2));
}
((ImageView)convertView.getTag(R.id.icon1)).setImageResource(R.drawable.icon);
((ImageView)convertView.getTag(R.id.icon2)).setImageResource(R.drawable.icon);
((TextView)convertView.getTag(R.id.text1)).setText(mData[position]);
((TextView)convertView.getTag(R.id.text2)).setText(mData[position]);
//停止计时
longendTime=System.nanoTime();
//计算耗时
longval=(endTime-startTime)/1000L;
Log.e("Test","Position:"+position+":"+val);
if(count<100){
if(val<1000L){
sum+=val;
count++;
}
}else
mTV.setText(String.valueOf(sum/100L)+":"+nullcount);//显示统计结果
returnconvertView;
}
2.3.2 测试结果(微秒除以1000,见代码)
第一次:450
第二次:467
第三次:472
第四次:451
第五次:441
四、总结
4.1 首先有一个认识是错误的,我们先来看截图:
可以发现,只有第一屏(可视范围)调用getView所消耗的时间远远多于后面的,通过对
convertView == null内代码监控也是同样的结果。
也就是说ListView仅仅缓存了可视范围内的View,随后的滚动都是对这些View进行数据更新。不管你有多少数据,他都只用ArrayList缓存可视范围内的View,这样保证了性能,也造成了我以为ListView只缓存View结构不缓存数据的假相(不会只有我一人这么认为吧- - #)。这也能解释为什么GOOGLE优化方案一比二高很多的原因。那么剩下的也就只有findViewById比较耗时了。据此大家可以看看AbsListView的源代码,看看
obtainView这个方法内的代码及RecycleBin这个类的实现,欢迎分享。
此外了解这个原理了,那么以下代码不运行你可能猜到结果了:
if(convertView==null){
convertView=mInflater.inflate(R.layout.list_item_icon_text,null);
((ImageView)convertView.findViewById(R.id.icon1)).setImageResource(R.drawable.icon);
((TextView)convertView.findViewById(R.id.text1)).setText(mData[position]);
((ImageView)convertView.findViewById(R.id.icon2)).setImageResource(R.drawable.icon);
((TextView)convertView.findViewById(R.id.text2)).setText(mData[position]);
}
else
returnconvertView;
没错,你会发现滚动时会重复显示第一屏的数据!
子控件里的事件因为是同一个控件,也可以直接放到convertView == null 代码块内部,如果需要交互数据比如position,可以通过tag方式来设置并获取当前数据。
4.2 本文方案一与方案二对比
这里推荐如果只是一般的应用(一般指子控件不多),无需都是用静态内部类来优化,使用第二种方案即可;反之,对性能要求较高时可采用。此外需要提醒的是这里也是用空间换时间的做法,View本身因为setTag而会占用更多的内存,还会增加代码量;而findViewById会临时消耗更多的内存,所以不可盲目使用,依实际情况而定。
4.3 方案三
此方案为“Henry Hu”提示,API Level 4以上支持,原理和方案三一致,减少findViewById次数,但是从测试结果来看效果并不理想,这里不再做进一步的测试。
五、推荐文章
Android,谁动了我的内存(1)
Android 内存泄漏调试
六、后期维护
2011-3-30 参见这里(http://www.javaeye.com/topic/971782)的讨论,据此将计划写续篇。
结束
对于Google I/O大会这个优化方案一直抱迟疑态度,此番测试总算是有了更进一步的了解,欢迎大家先测试后交流,看看还有什么办法能够再优化一点。
分享到:
相关推荐
listview和gridview切换,最好的优化
Android_ScrollView与ListView_GridView共存冲突解决方案,包括布局,资源多的卡死优化等!
react-native-easy-listview-gridview 这是React Native中ListView和GridView的包装库。样本列表显示网格视图特征拉刷新到达列表视图或网格视图的末尾时加载更多内容为空时列表视图或网格视图的自定义背景自定义样式...
pdf阅读,listview使用合集,gridview使用优化,最近开发比较繁忙在项目中用到的功能觉得还是不错的例子,希望能给您提供便利。
Android中ListView是使用平率最高的控件之一(GridView跟ListView是兄弟,都是继承AbsListView),ListView优化最有效的无非就是采用ViewHolder来减少频繁的对view查询和更新,缓存图片加快解码,减小图片尺寸。...
现在有一个需求:做一个服务器传过来一堆的.mp4播放链接,需要截取每个视频中的一帧,用来展示在列表上。 优化方面如下 ...5. 做了ListView、GridView、RecyclerView的滑动监听,减少重复代码,降低操作步骤。
当GridView或ListVIew每一项显示信息多或者显示图片时,在滚动是不加载资源,当停下来时,加载当前页面显示的几个Item资源,以节省内存资源。
[四次元]listview 适配器 优化 重用.rar [四次元]ListView+CheckBox UI 完美版。实现 全选 、 全不选 、 删除等功能.rar [四次元]ListView停止滚动开启下载图片.zip [四次元]Listview分页加载数据.rar [四次元]...
- ListView和GridView和TreeView - 无限的项目 - 项目在不同的大小(高度/宽度) - 具有不同预制的物品 - 在初始时间大小未知的项目 - 垂直滚动视图(从上到下,从下到上) - 水平滚动视图(从左到右,从右到...
—ListView和GridView ——无限的物品 -不同尺寸的物品(高度/宽度) -项目与不同的预制 —初始化时大小未知的项 -垂直列表视图(从上到下,从下到上) 水平列表视图(从左到右,从右到左) - GridView可以同时垂直和水平...
Android开源框架ImageLoader的完美例子,各种控件缓存listview,gridview.gallery viewpager
慕根 注意 ... // mCollectionView can be a ListView, GridView, RecyclerView or any instance of AbsListView! BaseAttacher attacher = Mugen . with(mCollectionView, new MugenCallbacks () {
在出现RecyclerView之前, ListView和GridView是用于显示项目列表的通用布局。 即使RecyclerView已优化了如何呈现项目列表的方式,而不必担心您希望如何显示它们(列表或网格),但在实现此类行为时却要付出一定的...
与ListView和GridView一样,RecyclerView需要额外的主体才能起作用,如果我们有LayoutManager(在视频中使用LinearLayoutManager),它负责重新定义设备屏幕上每个项目的视图。 我们有Adapter负责链接给他的最后一...
实例218 使用“…”优化GridView控件中的 超长字符显示 345 实例219 一次性编辑GridView控件的所有行 中的数据 347 实例220 嵌套DropDownList控件根据库存量 设置商品停售 348 实例221 嵌套CheckBox实现批量删除和...
综合运用UI界面设计、Sqlite数据存储、Activity(活动)、Service(服务)、MusicPlayer(音乐播放类)、ListView(列表)、GridView(网格列表)、Fragment(碎片)、BaseAdapter(适配器)等知识,设计开发一款具有...
用HorizontalScrollView实现了水平的Listview,代替Gridview来添加多张图片,并优化了内存。
一个通常的错误观念就是使用基本的布局结构(例如:LinearLayout、FrameLayout等)能够在大多数情况下 产生高效率 的布局。... 布局文件时重要的一点,例如在ListView或者GridView中使用时。 观察你的布局
对Android的RecyclerView,ListView,GridView,ScrollView的过度滚动支持... 该库提供了类似于iOS的过度滚动效果,几乎可以应用于所有Android本机可滚动视图。 它也可以很容易地适应自定义视图。 核心效果类是...