转载自:
农民伯伯: http://over140.cnblogs.com
前言
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
private TestAdapter mAdapter; private String[] mArrData; private TextView mTV; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mTV = (TextView) findViewById(R.id.tvShow); mArrData = new String[1000]; for (int i = 0; i < 1000; i++) { mArrData[i] = "Google IO Adapter" + i; } mAdapter = new TestAdapter(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 测试代码
private int count = 0; private long sum = 0L; @Override public View getView(int position, View convertView, ViewGroup parent) { //开始计时 long startTime = 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]); //停止计时 long endTime = System.nanoTime(); //计算耗时 long val = (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));//显示统计结果 return convertView; }
2.1.2 测试结果(微秒除以1000,见代码)
次数 |
4个子元素 |
10个子元素 |
第一次 |
366 |
723 |
第二次 |
356 |
689 |
第三次 |
371 |
692 |
第四次 |
356 |
696 |
第五次 |
371 |
662 |
按照Google I/O介绍的第三种方案,是把item子元素分别改为4个和10个。
2.2.1 测试代码
private int count = 0; private long sum = 0L; @Override public View getView(int position, View convertView, ViewGroup parent) { // 开始计时 long startTime = System.nanoTime(); ViewHolder holder; if (convertView == null) { convertView = mInflater.inflate(R.layout.list_item_icon_text, null); holder = new ViewHolder(); 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]); // 停止计时 long endTime = System.nanoTime(); // 计算耗时 long val = (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));// 显示统计结果 return convertView; } } static class ViewHolder { TextView text1; ImageView icon1; TextView text2; ImageView icon2; }
2.2.2 测试结果(微秒除以1000,见代码)
次数 |
4个子元素 |
10个子元素 |
第一次 |
311 |
417 |
第二次 |
291 |
441 |
第三次 |
302 |
462 |
第四次 |
286 |
444 |
第五次 |
299 |
436 |
此方案为“Henry Hu”提示,API Level 4以上提供,这里顺带测试了一下不使用静态内部类情况下性能。
@Override public View getView(int position, View convertView, ViewGroup parent) { // 开始计时 long startTime = 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]); // 停止计时 long endTime = System.nanoTime(); // 计算耗时 long val = (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);// 显示统计结果 return convertView; }
2.3.2 测试结果(微秒除以1000,见代码)
第一次:450
第二次:467
第三次:472
第四次:451
四、总结
4.1 首先有一个认识是错误的,我们先来看截图:
可以发现,只有第一屏(可视范围)调用getView所消耗的时间远远多于后面的,通过对
此外了解这个原理了,那么以下代码不运行你可能猜到结果了:
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 return convertView;
没错,你会发现滚动时会重复显示第一屏的数据!
子控件里的事件因为是同一个控件,也可以直接放到convertView == null 代码块内部,如果需要交互数据比如position,可以通过tag方式来设置并获取当前数据。
4.2 本文方案一与方案二对比
这里推荐如果只是一般的应用(一般指子控件不多),无需都是用静态内部类来优化,使用第二种方案即可;反之,对性能要求较高时可采用。此外需要提醒的是这里也是用空间换时间的做法,View本身因为setTag而会占用更多的内存,还会增加代码量;而findViewById会临时消耗更多的内存,所以不可盲目使用,依实际情况而定。
4.3 方案三
此方案为“Henry Hu”提示,API Level 4以上支持,原理和方案三一致,减少findViewById次数,但是从测试结果来看效果并不理想,这里不再做进一步的测试。
五、推荐文章
2011-3-30 参见这里(http://www.javaeye.com/topic/971782)的讨论,据此将计划写续篇。
结束
对于Google I/O大会这个优化方案一直抱迟疑态度,此番测试总算是有了更进一步的了解,欢迎大家先测试后交流,看看还有什么办法能够再优化一点。
相关推荐
ListView 视图缓存性能优化 ListView 是 Android 中最常用的控件,通过适配器来进行数据适配然后显示出来,而其性能是个很值得研究的话题。在 Android 开发中,ListView 的性能优化是一个重要的方面,本文将讨论 ...
在Android开发中,ListView是应用中最常用的控件之一,它用于展示大量的数据列表。然而,随着数据量的增加,ListView可能会出现性能问题,如滚动不流畅、内存消耗过大等。因此,对ListView进行性能优化至关重要。...
5. **ListView的复用机制**:ListView的视图复用是其核心优化之一,通过convertView参数在`getView()`方法中重用已创建的View,避免为每个列表项创建新的对象,减少内存开销。 6. **延时加载(Lazy Loading)**:只...
为了优化性能,ListView使用了视图复用机制(ViewHolder模式),避免频繁地创建和销毁视图。开发者可以创建一个ViewHolder类,保存每个视图的引用,这样在`getView()`方法中就可以快速访问和更新视图状态,而无需...
- **复用视图**:为了提高性能,ListView使用了视图缓存机制。在`getView()`中,我们检查`convertView`是否为空,如果非空,则重用已有的视图,避免频繁创建新视图。 - **ViewHolder模式**:为了进一步提升性能,...
ListView是Android平台上常见的一种用于展示大量数据的控件,它通过滚动的方式节省屏幕空间,但同时也带来了性能问题。本示例“ListView高级部分,性能优化的demo”将深入探讨六个关键领域的优化策略,帮助开发者...
综上所述,"Android ListView 实例源码"应包含了ListView的基本用法、自定义Adapter的实现、事件监听、性能优化等多个方面。通过研究这个源码,开发者可以深入理解ListView的工作原理,并学习如何在实际项目中高效地...
除了使用缓存库,还可以结合以下策略进一步优化ListView的性能: - 使用ViewHolder模式减少findViewById的次数。 - 设置合适的ListView项高度,避免过度绘制。 - 使用异步加载库,如Glide或Picasso,它们也支持内存...
本篇文章将深入探讨“android listview”相关的知识点,包括横向ListView、微信风格的ListView以及XListView的优化。 首先,我们来看“横向ListView”。在Android原生的ListView通常是垂直滚动的,但有时我们可能...
在Android开发中,ListView是常用的一种视图组件,用于展示大量数据列表,用户可以通过滚动查看更多的条目。然而,ListView的性能优化是一个重要的主题,因为如果不进行优化,它可能会导致应用性能下降,甚至出现...
9. **优化性能**: - 实现ViewHolder模式,减少对象的创建和查找,提高ListView滚动的流畅性。 - 使用`convertView.setTag()`和`ViewHolder.getTag()`来缓存子视图,避免每次`getView()`都去查找子视图。 通过...
综上所述,通过ListView的优化、图片的异步加载和缓存策略,以及避免图片闪烁的方法,可以显著提升Android应用的性能和用户体验。实际开发中,结合具体情况灵活运用这些技术,能有效地解决类似问题。
总的来说,这份资料包提供了关于ListView图片加载优化的一个实例,帮助开发者理解和实现懒加载和缓存策略,提升应用性能。通过深入学习和实践,你可以更好地掌握Android应用中的图片加载优化技巧。
在Android开发中,ListView是展示大量数据常用的组件,但如果不进行优化,特别是在处理图片加载时,用户滚动ListView可能会出现卡顿现象。本教程将重点讲解如何通过异步加载技术优化ListView,确保滚动流畅,提高...
本文将深入探讨如何实现一个高性能的ListView,结合线程池和缓存管理来优化网络图片的加载。 首先,理解ListView的滚动优化至关重要。当ListView滚动时,为了提高性能,只有当前可见的item会进行渲染。这意味着我们...
为了优化性能,我们还可以使用ViewHolder模式来缓存视图的引用,避免频繁的findViewById操作。同时,对于大量的子项,可以考虑使用DiffUtil来减少不必要的视图刷新。 总结来说,实现Android的分组ListView并支持...
ListView是Android开发中处理长列表的标准方式,它可以有效地管理视图的创建和复用,以优化性能。在实际开发中,开发者还需要关注如何正确适配ListView的数据源(如ArrayAdapter或CursorAdapter),以及处理点击事件...
这份"Android listview获取网络图片缓存优化"的源码Demo就是针对这一问题进行讲解和示例的。 1. **内存缓存**:在Android中,我们可以使用内存缓存来快速加载图片。当图片首次下载后,将其存储在内存中,下次需要时...