Android杂谈--内存泄露(1)--contentView缓存使用与ListView优化-点点未来-天下标王
Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1089408
  • 博文数量: 403
  • 博客积分: 10272
  • 博客等级: 上将
  • 技术积分: 4407
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-24 14:22
文章分类

全部博文(403)

文章存档

2012年(403)

分类: 嵌入式

2012-04-10 19:24:23

引起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后基本完成时暂停的时间

详细可参考:

 

(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秒

当拉到1000的时候,堆内存总计已经申请了9607K,已用内存7245K,明显已经比刚开始时要大了 ,耗时:3435241667ns=3.435241667秒

当拉到2000的时候,堆内存总计13511K,已用内存11153K,耗时:6660369835ns = 6.660369835秒

---------------------------我又创建了10000个ListView,测试后直到内存泄露,证明峰值却是是32M,而 不使用convertView导致的内存泄露,当内存泄露时手机会提示force close,并将错误写入/data/anr/traces.txt中,你可以adb pull下来查看具体信息

(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秒之多。

当position为2000的时候,已用内存只有3068K,堆总共内存6215K,而且external memory是0K,用时:378326396ns = 0.378326396秒,性能差距如此之大,都有点不敢相信。也不知道这种方式对不对,如有不妥的地方,还希望大牛能给出正确回答

(3)使用contentView+静态类ViewHolder类

  通过convertView+ViewHolder来实现,ViewHolder就是一个静态类,使用 ViewHolder 的关键好处是缓存了显示数据的视图(View),加快了 UI 的响应速度。

当我们判断 convertView == null  的时候,如果为空,就会根据设计好的List的Item布局 (XML),来为convertView赋值,并生成一个viewHolder来绑定converView里面的各个View控件(XML布局里面的那些 控件)。再用convertView的setTag将viewHolder设置到Tag中,以便系统第二次绘制ListView时从Tag中取出。(看下 面代码中)

如果convertView不为空的时候,就会直接用convertView的getTag(),来获得一个ViewHolder。

静态类ViewHolder

//定义静态类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理想的多

当position为2000时,用时:336669887ns = 0.336669887秒,比直接使用convertView的方式稍微好一点点,不过性能相差不多

阅读(2355) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~

相关内容推荐

大良网站优化哪家便宜seo网站优化套餐从江seo网站优化价格石城网站搜索引擎优化阳泉公司网站优化黄陂网站优化阳谷县网站优化报价网站优化站内 站外的策划方案海口网站优化操作知乎回答有利于网站有优化吗讷河网站优化电话长春好的网站推广优化安康网站优化排名策划达州网站优化教程邯郸家装行业网站优化推广可靠吗优化网站排名 推荐云尚网络网站优化页面多好还是页面少好网站优化制作公司哪家强如何写优化网站文章泰州公司优化网站网站怎么优化自己免费网站竞价优化案例哈尔滨新网站优化运营公司肇庆餐饮网站seo优化网站导航设计优化新密网站优化哪家好网站没做过优化会有排名吗网站的优化就选j火9星网站百度技术SEO优化湖南网站优化搜行者SEO凌源360网站优化关于xx网站的优化诊断网站建设与优化方案成都市网站优化价格网站内的优化两部分南通外贸网站优化专业优化网站在线咨询新乡优化网站排名价格多少公司网站优化廊坊壹起航网站优化怎么设置做百度小程序有利于网站优化嘛好的销售行业网站优化平台网站推广优化怎么提升成单量机械网站优化学习金华网站优化公司最新报价潼南seo网站优化师娃哈哈网站优化建议网站优化descri湖州网站优化案例推荐服装行业网站优化公司优化网站排名哪个品牌好聊城质量好网站优化武侯区网站推广优化福州优化网站有哪些渠道邹平怎样优化自己网站张店网站优化找网泰科技揭阳网站站内优化茶山网站建设优化cms建的网站好优化吗北京信息化网站优化大全宁海seo网站关键词优化手机网站优化哪家价格便宜九江网站优化注意事项搜索引擎优化和网站运营苏州吴中网站优化兰州模板网站优化服务虞城网站优化推广哪家好网站的优化精通火24星天津河北外贸网站优化中小型网站建设优化芜湖网站优化怎么选网站多久能优化到第一页东坑电子网站优化都有什么服务中山网站优化营销中心普定优化推广网站德州知名网站优化制作衡阳娄底网站优化中山电器建材网站seo优化徐州网站优化渠道福田软件网站优化的有效方式阳江网站优化关键词费用新沂网站优化工作室网站内部优化流程东门免费的网站优化怎么对一个网站做优化陕西价格低的网站优化优化检测网站黄山网站排名优化模式有哪些潮州网站怎么优化十堰网站排名优化哪家好双辽百度网站优化沈阳网站优化好吗电商新媒体网站优化提高收录深圳综合网站优化哪个好涧西网站优化找哪家网站优化怎么做排名网站制作优化忧咐乚云速捷网站信息优化规则4点提升网站优化建议长沙网站快照优化天津网站优化推广哪家好网站优化批发网站负面优化seo全面剖析网站性能优化如何优化的江都如何优化网站江西网站关键词排名优化策略上犹县网站seo优化排名千阳县网站seo优化排名玻璃机械网站seo优化多少钱北京网站关键词优化哪家好渭南网站优化怎么样网站优化报告的结果分析泰安网站优化哪家便宜柘城专业网站优化怎么收费石城网站搜索引擎优化海南seo网站优化网站链接优化主要内容武隆区网站seo优化排名如何优化网站文章有效南山网站优化服务seo怎么优化网站讲课解后缀top的域名网站好优化吗金华网站优化公司最新报价优化公司网站郧云速捷三无网站seo优化怎么设置网站优化利润优化网站降权怎么恢复seo网站优化加盟费用福州晋安网站seo优化哪家强辽源网站优化公司电话广东企业网站优化服务如何将网站优化到百度首页网站怎么样优化的海口市网站seo优化排名优化网站顶火16星高尚蔡甸网站优化评价和优化网站开始云速 捷官方网站优化的好处新乡整站网站优化排名莱芜优化网站电话酷网网赚网站优化海伦网站网址优化阳江seo网站优化什么价格喀什seo网站优化安顺市网站优化渠道莱芜济南网站优化哪家技术好保定百度网站快速优化汉阳网站关键词优化公司医疗网站优化怎么做泸州网站seo优化网站做优化是什么意思南通外贸网站优化吉水网站优化排名邵阳网站优化方案江东区网站优化郑州移动网站优化价格网站SEO优化总结和体会有实力的网站优化哪里不错seo优化网站设计封丘网站优化公司多少钱晋城网站按年优化网站快速优化益处易 速达乌兰察布专业网站优化北极星网站能优化简历吗网站标题优化的方法黄山网站优化怎么做汕尾网站优化常用方法优化网站方云速捷优秀同安区网站优化公司白帽seo网站优化沧州百度网站优化江西网站关键词排名优化策略南阳律师网站优化专门的尼康优化校准网站专门做网站优化在线推广创业港哥分享网站优化seo提升网站转化率优化策略衡水网站优化多少钱专业网站优化哪家专业泰州网站权重优化网站找人优化关键词成都网站优化方法封丘县网站seo优化排名济南网站自己优化新乡网站优化电池充电临沂网站优化软件禄劝网站优化渠道东门免费的网站优化网站优化基础知识优惠了解深圳网站优化推广网站优化和微信推广的区别毕节手机网站优化唐山seo网站优化喀什网站优化定制优化网站的原则网站建设优化优惠火7星怎么对一个网站做优化周口网站建设优化株洲网站建设优化正规的网站优化哪家靠谱

合作伙伴

天下标王

龙岗网络公司
深圳网站优化
龙岗网站建设
坪山网站建设
百度标王推广
天下网标王
SEO优化按天计费
SEO按天计费系统