引起Android内存泄露有很多种原因,下面罗列了一些问题,以后会一一解决
1、构造Adapter时没有使用缓存convertView(衍生出ListView优化问题)
2、查询数据库游标没有关闭
3、Activity中生命周期对象大于Activity生命周期(关于Application Context与Activity Context)
4、Bitmap对象不使用时没有recycle掉(这里还有其他解决方案)
今天说的是第一种:如何使用缓存来优化ListView
因为如果不使用缓存convertView的话,调用getView时每次都会重新创建View,这样之前的View可能还没有销毁,加之不断的新建View势必会造成内存泄露。
使用getView时有3方案:(1)没有使用convertView,(2)使用convertView,(3)使用convertView+静态类ViewHolder
我做了一个测试,代码在下面,创建2000个View,从0拉到最后,计算总共耗,同时显示GC释放内存的大小,三种测试的结果如下:
注:这里先说下 GC_EXTERNAL_ALLOC freed 7K, 18% free 11153K/13511K, external 1632K/1672K, paused 89ms 的意思
在Dalvik中,为一个程序分配的内存要根据机型的不同而不同,一般为32M,而虚拟机会把这些内存分别分配给,JAVA使用的堆内存(heap)和Nativie使用的内存(external)(即虚拟机中通过JNI调用本地Nativie的类中malloc分配的内存,如Bitmap,java.nio.ByteBuffers)。不过两者不同共享,也就是说Native的内存不够用了,而JAVA内存够用时是不能向JAVA申请的,必须向虚拟机申请才行,当虚拟机无法分配的时候就会报OOM的错误
freed 7k:表示GC已经释放了7K的内存
18% free 11153K/13511K:表示JAVA使用的堆内存(对象存在于此),18% free表示当前剩余18%的堆内存(heap memory),11153K表示当前已用的堆内存,13511K表示堆内存总共大小(网上有些文章这部分弄错了,很多转载都是同一个)
external 1632K/1672K:1632K表示已用external memory,总共1672K external memory(注意:这个可能只存在于Android 3.0之前)
paused 89ms:这里其实包括了两部分,一个是在调用GC之前暂停的时间,一个是调用GC后基本完成时暂停的时间
详细可参考:http://stackoverflow.com/questions/4550757/android-logs-gc-external-alloc-gc-for-malloc
(1)没有使用convertView
没有任何处理,不建议这样写。如果数据量少可以,但是如果列表项数据量很大的时候,会每次都重新创建View,设置资源,严重影响性能,所以从一开始就不要用这种方式
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- //Get a View that displays the data at the specified position in the data set.
- //开始计时,性能测试用nanoTime会更精确,因为它是纳秒级的
- long startTime = System.nanoTime();
- View item = mInflater.inflate(R.layout.list_item, null);
- ImageView img = (ImageView)item.findViewById(R.id.img);
- TextView title = (TextView)item.findViewById(R.id.title);
- TextView info = (TextView)item.findViewById(R.id.info);
- img.setImageResource(R.drawable.ic_launcher);
- title.setText("loulijun");
- info.setText("www.cnblogs.com/loulijun");
- //停止计时
- long endTime = System.nanoTime();
- //耗时
- long spendTime = (endTime - startTime);
- sumTime += spendTime;
- Log.d("GoogleIO", "position at:"+position+"--sumTime:"+String.valueOf(sumTime));
- return item;
- }
测试结果:
目前VM只为他们分配了5767K+518k的内存,而内存峰值是32M
刚开始时,而且heap memory只申请了5767K,已用内存3353K,注意数据大小的变化:耗时:167633055ns = 0.167633055秒
<ignore_js_op>
当拉到1000的时候,堆内存总计已经申请了9607K,已用内存7245K,明显已经比刚开始时要大了 ,耗时:3435241667ns=3.435241667秒
<ignore_js_op>
当拉到2000的时候,堆内存总计13511K,已用内存11153K,耗时:6660369835ns = 6.660369835秒
<ignore_js_op>
---------------------------
又创建了10000个ListView,测试后直到内存泄露,证明峰值却是是32M,而不使用convertView导致的内存泄露,当内存泄露时手机会提示force close,并将错误写入/data/anr/traces.txt中,你可以adb pull下来查看具体信息
<ignore_js_op>
(2)使用convertView后的测试数据(优化后)
通过缓存convertView,convertView可以缓存可视范围内的convertView,当再次向下滑动时又开始更新View,这种利用缓存convertView的方式可以判断如果缓存中不存在View才创建View,如果已经存在可以利用缓存中的View,这样会减少很多View的创建,提升了性能
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- //Get a View that displays the data at the specified position in the data set.
- if(convertView == null)
- {
- convertView = mInflater.inflate(R.layout.list_item, null);
- }
- //开始计时,性能测试用nanoTime会更精确,因为它是纳秒级的
- long startTime = System.nanoTime();
- ImageView img = (ImageView)convertView.findViewById(R.id.img);
- TextView title = (TextView)convertView.findViewById(R.id.title);
- TextView info = (TextView)convertView.findViewById(R.id.info);
- img.setImageResource(R.drawable.ic_launcher);
- title.setText("loulijun");
- info.setText("www.cnblogs.com/loulijun");
- //停止计时
- long endTime = System.nanoTime();
- //耗时
- long spendTime = (endTime - startTime);
- sumTime += spendTime;
- Log.d("GoogleIO", "position at:"+position+"--sumTime:"+String.valueOf(sumTime));
- return convertView;
- }
测试数据我还是用2000吧,10000太大了(一万年太久,只争朝夕)
测试结果:
这次一直拉到最后明显比刚才流畅多了,而且GC释放内存的次数也明显少了很多,最后用的时间和当前使用的内存也小很多,优化后的确好多了
当position为1000的时候,附近没怎么调用GC,用时:213653551ns=0.213653551秒,额,差距有点大,上面到达1000时用时达到3.43秒之多。
<ignore_js_op>
当position为2000的时候,已用内存只有3068K,堆总共内存6215K,而且external memory是0K,用时:378326396ns = 0.378326396秒,性能差距如此之大,都有点不敢相信。也不知道这种方式对不对,如有不妥的地方,还希望大牛能给出正确回答
<ignore_js_op>
(3)使用contentView+静态类ViewHolder类
通过convertView+ViewHolder来实现,ViewHolder就是一个静态类,使用 ViewHolder 的关键好处是缓存了显示数据的视图(View),加快了 UI 的响应速度。
- //定义静态类ViewHolder
- static class ViewHolder
- {
- public ImageView img;
- public TextView title;
- public TextView info;
- }
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- //Get a View that displays the data at the specified position in the data set.
- //开始计时,性能测试用nanoTime会更精确,因为它是纳秒级的
- long startTime = System.nanoTime();
- ViewHolder holder;
- if(convertView == null)
- {
- holder = new ViewHolder();
- convertView = mInflater.inflate(R.layout.list_item, null);
- holder.img = (ImageView)convertView.findViewById(R.id.img);
- holder.title = (TextView)convertView.findViewById(R.id.title);
- holder.info = (TextView)convertView.findViewById(R.id.info);
- convertView.setTag(holder);
- }else
- {
- holder = (ViewHolder)convertView.getTag();
- holder.img.setImageResource(R.drawable.ic_launcher);
- holder.title.setText("loulijun");
- holder.info.setText("www.cnblogs.com/loulijun");
- }
- //停止计时
- long endTime = System.nanoTime();
- //耗时
- long spendTime = (endTime - startTime);
- sumTime += spendTime;
- Log.d("GoogleIO", "position at:"+position+"--sumTime:"+String.valueOf(sumTime));
- return convertView;
- }
到这里,可能会有人问ViewHolder静态类结合缓存convertView与直接使用convertView有什么区别吗,是否重复了
在这里,官方给出了解释
提升Adapter的两种方法
To work efficiently the adapter implemented here uses two techniques:
-It reuses the convertView passed to getView() to avoid inflating View when it is not necessary
(译:重用缓存convertView传递给getView()方法来避免填充不必要的视图)
-It uses the ViewHolder pattern to avoid calling findViewById() when it is not necessary
(译:使用ViewHolder模式来避免没有必要的调用findViewById():因为太多的findViewById也会影响性能)
ViewHolder类的作用
-The ViewHolder pattern consists in storing a data structure in the tag of the view
returned by getView().This data structures contains references to the views we want to bind data to,
thus avoiding calling to findViewById() every time getView() is invoked
(译:ViewHolder模式通过getView()方法返回的视图的标签(Tag)中存储一个数据结构,这个数据结构包含了指向我们
要绑定数据的视图的引用,从而避免每次调用getView()的时候调用findViewById())
测试数据:(跟直接使用convertView数据相差不多)
当position为1000时,用时:199188216ns = 0.199188216秒,堆内存的时候也没比没有使用convertView理想的多
<ignore_js_op>
当position为2000时,用时:336669887ns = 0.336669887秒,比直接使用convertView的方式稍微好一点点,不过性能相差不多
<ignore_js_op>
相关推荐
为了解决这个问题,开发者通常会采用图片缓存技术进行优化。本示例“listview优化图片缓存例子”将详细介绍如何利用缓存策略来提高ListView加载图片的效率。 一、图片缓存的重要性 在ListView中,由于屏幕空间有限...
这个"Android高级应用源码-listview获取网络图片缓存优化.zip"是一个示例项目,旨在解决这些问题,通过缓存机制优化ListView中加载网络图片的过程。 首先,我们来探讨一下ListView的优化策略。ListView的优化主要...
在Android开发中,ListView是展示大量数据常用的组件,但如果不进行优化,很容易引发内存溢出和界面卡顿问题,特别是当列表中包含大图时。本文将深入探讨如何对ListView进行内存优化,确保10M级别的图片加载时,应用...
ListView控件是Windows Forms开发中常用的一个组件,用于显示数据集合。在VB.NET环境中,当ListView控件包含大量数据或需要频繁...在实际开发中,理解并运用双缓存技术不仅能优化程序性能,还能提升软件的整体质量。
因此,对ListView中获取网络图片进行缓存优化是非常必要的。这个"应用源码之listview获取网络图片缓存优化.zip"压缩包很可能包含了关于如何高效处理这个问题的源码示例。 首先,我们来探讨一下图片缓存的两个主要...
以上是关于"listview获取网络图片缓存优化"的详细知识点介绍,这些技术不仅可以应用于ListView,也可以推广到其他需要加载网络资源的场景,如RecyclerView、GridView等。通过理解和实践这些优化技巧,开发者可以提升...
本压缩包"安卓Android源码——listview获取网络图片缓存优化.zip"显然是为了演示如何优化ListView中加载网络图片的过程,通过缓存策略来提高用户体验。其中包含的项目名为"LazyLoaderDemo",暗示了这是一个懒加载的...
本篇将深入探讨如何对ListView进行优化,以及如何实现图片的异步加载和缓存策略,以避免这些问题。 **1. ListView优化** ListView的优化主要涉及以下几个方面: 1.1 **convertView复用机制**:ListView的Adapter...
本资料"Android应用源码之listview获取网络图片缓存优化.zip"主要探讨了如何通过优化代码来解决这些问题,提升用户体验。 首先,我们需要理解ListView的工作原理。ListView通过复用ViewHolder中的视图来提高滚动...
"listview获取网络图片缓存优化"这个主题旨在解决这些问题,通过实现一个名为`LazyLoaderDemo`的示例来演示如何高效地加载和缓存网络图片。 首先,我们需要理解ListView的工作原理。ListView采用视图复用机制,即...
本篇将深入探讨如何通过源码分析和实践,对ListView加载网络图片进行缓存优化。 1. 图片下载与缓存基础 - **异步加载**:为了不阻塞主线程,通常我们会使用AsyncTask或Loader在后台线程下载图片。 - **内存缓存**...
总结起来,"ListView异步加载图片三级缓存"是一个重要的Android性能优化技巧,它结合了异步处理、内存管理、磁盘操作以及网络通信,为用户提供流畅、高效的图片浏览体验。在LazyLoaderDemo这样的示例代码中,我们...
本压缩包"安卓Android源码——listview获取网络图片缓存优化.rar"提供了一个名为"LazyLoaderDemo"的示例,它展示了如何通过懒加载技术来优化ListView中加载网络图片的过程,以提升用户体验。 首先,我们来看一下...
在CocosCreator中,ListView是实现数据列表展示的重要组件,特别是在处理大量数据时,优化ListView的性能至关重要。本文将深入探讨ListView的优化策略,以及如何通过`GListView.js`这样的扩展来提升显示效率。 首先...
5. **图片重用机制**:ListView的一项优化技术是 convertView 重用,通过复用已经滑出屏幕的item视图,减少对象创建,提高性能。 6. **图片压缩**:为了节省内存和磁盘空间,加载图片前可以对其进行压缩,如调整...
综上所述,优化Android ListView中网络图片的加载涉及到多个层次的技术,包括异步加载、缓存策略、图片库选择、Bitmap配置、ViewHolder模式以及适配器和滚动事件的处理。通过综合运用这些技术,我们可以显著提高...
listview获取网络图片缓存优化.zip项目安卓应用源码下载listview获取网络图片缓存优化.zip项目安卓应用源码下载 1.适合学生毕业设计研究参考 2.适合个人学习研究参考 3.适合公司开发项目技术参考
总之,双缓冲技术在ListView的优化中起着关键作用,通过合理的数据加载和视图复用策略,能够显著提升列表滚动的流畅性和整体应用的性能。对于包含大量图片或者其他资源的列表,这种优化尤为重要。