0.前言  

上一篇常见的内存泄漏以及解决方案(一) 中已经对部分可能会引发内存泄漏的情况进行了阐述,此篇将从图片、动画等资源角度介绍可能会造成内存泄漏的情况以及应对方法。

6. 集合类导致内存泄漏

很常见的一个例子就是图片的三级缓存结构,为了更好的用户体验,缓存机制必不可少,三级缓存分别为网络缓存,本地缓存以及内存缓存。在内存缓存逻辑类中,通常会定义这样的集合类。

private HashMap<String, Bitmap> mMemoryCache = new HashMap<String, Bitmap>();//String类为该图片对应url

三级缓存结构过程介绍:

在用户切换到展示图片的界面时,当然是优先判断内存缓存是否为Null,不为空直接展示图片,若为空,同样的逻辑去判断本地缓存(不为空便设置内存缓存并展示图片),本地缓存再为空才会根据该图片的url用网络下载类去下载该图片并展示图片(当然了,下载到图片后会有设置本地缓存以及内存缓存的操作)。

内存泄漏的问题就出现在内存缓存中:只要HashMap对象实例被引用,而Bitmap对象又都是强引用,Bitmap中图片越来越多,即便是内存溢出了,垃圾回收器也不会处理(也有回收延迟问题)。

解决方案:

(1)我们可以选择使用软引用,从而在内存不足时,垃圾回收器更容易回收Bitmap垃圾。

private HashMap<String, SoftReference<Bitmap>> mMemoryCache = new HashMap<String, SoftReference<Bitmap>>();

(2)Android2.3以后,SoftReference不再可靠。垃圾回收期更容易回收它,不再是内存不足时才回收软引用。那么缓存机制便失去了意义。Google官方建议使用LruCache作为缓存的集合类。

其实内部封装了LinkedHashMap。内部原理是一直判断集合大小是否超出给定的最大值,超出就把最早最少使用的对象踢出集合。

private LruCache<String, Bitmap> mMemoryCache = new LruCache<String, Bitmap>
((int)(Runtime.getRuntime().maxMemory()/8)){
//用最大内存的1/8分配给这个集合使用
//让这个集合知道每个图片的大小
@Override
protected int sizeOf(String key, Bitmap value){
int byteCount = value.getRowBytes() * value.getHeight();//计算图片大小,每行字节数*高度
return byteCount;
}
};

7. Bitmap优化

Android中很多控件比如ListView/GridView/ViewPaper通常都会包含很多图片,特别是快速滑动的时候可能加载大量的图片,因此对图片进行优化处理显得尤为重要。

对于图片,当然也可以使用压缩以及回收的策略来尽量避免内存溢出。

7. 1 Bitmap压缩

压缩即把图片的体积缩小,一方面可以减小APK的大小,另一方面就是将图片加载入内存后减少内存的占用,从而间接地减少内存溢出的可能性。对部分图片压缩的知识已经在Android开发——减小APK大小中介绍过了,这里就不再赘述。

这里主要说一下通过设置参数进行压缩的方法。使用BitmapFactory.Options设置inSampleSize(表示缩略图大小为原始图片大小的几分之一)就可以缩小图片。如果该值为2,则缩略图的宽和高都是原始图片的1/2,图片的大小就为原始大小的1/4(小于等于1不缩放)。

使用BitmapFactory.Options设置inJustDecodeBounds为true后,再使用decode系列方法,并不会真正的分配空间,即解码出来的Bitmap为null,但是可计算出原始图片的宽度和高度,即options.outWidth和options.outHeight。通过这两个值,就可以知道图片是否过大了。

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

并提供了一个calculateInSampleSize()工具方法来帮我们动态计算并返回inSampleSize。

public static int calculateInSampleSize( //参2和3为ImageView期待的图片大小
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// 图片的实际大小
final int height = options.outHeight;
final int width = options.outWidth;
//默认值
int inSampleSize = 1;
//动态计算inSampleSize的值
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height/2;
final int halfWidth = width/2;
while( (halfHeight/inSampleSize) >= reqHeight && (halfWidth/inSampleSize) >= reqWidth){
inSampleSize *= 2;
}
}
return inSampleSize;
}

创建一个完整的缩略图方案:

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) { final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options); // 计算inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // 别忘记将opts.inJustDecodeBound设置回false。否则获取的bitmap对象还是null
options.inJustDecodeBounds = false;
//重新加载图片
return BitmapFactory.decodeResource(res, resId, options);
}

当我们在使用ImageView进行设置图片资源时:

mImageView.setImageBitmap( //ImageView所期望的图片大小为100*100像素
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

7. 2 Bitmap回收

在2.3以下的系统中,Bitmap的像素数据存储在native中,Bitmap对象存储在Java堆中的,所以在回收Bitmap时,需要回收两个部分的空间:native和Java堆。 即先调用recycle()释放native中Bitmap的像素数据,再对Bitmap对象置null以保证GC对Bitmap对象的回收。

if(bitmap != null && !bitmap.isRecycled()){
// 回收并且置为null
bitmap.recycle();
bitmap = null;
}
System.gc();//并不能保证立即开始回收,而是加快回收的到来

在3.0以上的系统中,Bitmap的像素数据和对象本身都是存储在Java堆中的,无需主动调用recycle(),只需将对象置null,由GC自动管理。

8. 属性动画导致内存泄漏

Android3.0开始支持的属性动画中有一类无限循环的动画,它会通过View间接持有Activity的引用,如果没有在onDestroy中停止动画,就会泄漏当前的Activity。

解决方案:

在onDestroy方法中调用animator.cancel();来停止动画。

9.资源未关闭导致内存泄漏

当我们打开资源时,一般都会使用缓存。比如读写文件资源、打开数据库资源等等。当我们不再使用时,应该及时地关闭它们,使得缓存内存区域及时回收。虽然有些对象,如果我们不去关闭,它自己在finalize()函数中会自行关闭。但是这得等到GC回收时才关闭,这样会导致其在缓存中驻留一段时间。如果我们频繁的打开资源,内存泄漏带来的影响就比较明显了。

解决方案:

及时关闭我们不再使用的资源。比如查询数据库后没有关闭游标cursor、构造Adapter时没有使用convertView重用控件、使用Bitmap及时调用recycle()。

至此关于Android内存泄漏的内容总结完毕。

转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52351062

Android开发——常见的内存泄漏以及解决方案(二)的更多相关文章

  1. Android开发——常见的内存泄漏以及解决方案(一)

    0. 前言   转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52333954 Android的内存泄漏是Android开发领域永恒的 ...

  2. Android开发 |常见的内存泄漏问题及解决办法

    在Android开发中,内存泄漏是比较常见的问题,有过一些Android编程经历的童鞋应该都遇到过,但为什么会出现内存泄漏呢?内存泄漏又有什么影响呢? 在Android程序开发中,当一个对象已经不需要 ...

  3. Android中常见的内存泄漏

    为什么会产生内存泄漏? 当一个对象已经不需要再使用了,本该被回收时,而有另外一个正在使用的对象持有它的引用从而导致它不能被回收,这导致本该被回收的对象不能被回收而停留在堆内存中,这就产生了内存泄漏. ...

  4. android中常见的内存泄漏和解决的方法

    android中的内存溢出预计大多数人在写代码的时候都出现过,事实上突然认为工作一年和工作三年的差别是什么呢.事实上干的工作或许都一样,产品汪看到的结果也都一样,那差别就是速度和质量了. 写在前面的一 ...

  5. Android性能优化之常见的内存泄漏

    前言 对于内存泄漏,我想大家在开发中肯定都遇到过,只不过内存泄漏对我们来说并不是可见的,因为它是在堆中活动,而要想检测程序中是否有内存泄漏的产生,通常我们可以借助LeakCanary.MAT等工具来检 ...

  6. 老李分享:Android性能优化之内存泄漏1

    老李分享:Android性能优化之内存泄漏   前言 对于内存泄漏,我想大家在开发中肯定都遇到过,只不过内存泄漏对我们来说并不是可见的,因为它是在堆中活动,而要想检测程序中是否有内存泄漏的产生,通常我 ...

  7. 5个Android开发中比较常见的内存泄漏问题及解决办法

    android中一个对象已经不需要了,但是其他对象还持有他的引用,导致他不能回收,导致这个对象暂存在内存中,这样内存泄漏就出现了.   内存泄漏出现多了,会是应用占用过多的没存,当占用的内存超过了系统 ...

  8. Android开发常见的Activity中内存泄漏及解决办法

    上一篇文章楼主提到由Context引发的内存泄漏,在这一篇文章里,我们来谈谈Android开发中常见的Activity内存泄漏及解决办法.本文将会以“为什么”“怎么解决”的方式来介绍这几种内存泄漏. ...

  9. JavaScript如何工作:内存管理+如何处理4个常见的内存泄漏

    摘要: 作者将自己常用的JavaScript模块分享给大家. 原文:JavaScript如何工作:内存管理+如何处理4个常见的内存泄漏 作者:前端小智 Fundebug经授权转载,版权归原作者所有. ...

随机推荐

  1. OC与JS交互前言

    OC与JS交互过程中,可能会需要使用本地image资源及html,css,js文件,这些资源应该如何被加载? 一.WebView加载HTML UIWebView提供了三个方法来加载html资源 1.  ...

  2. Java 记录日志

    Java9的日志级别: ALL 最低级别,系统会输出所有的日志信息,会生成大量的·.冗余的日志 TRACE 输出系统的各种跟踪信息,会生成大量的·.冗余的日志 DEBUG 输出调试信息,会生成较多的日 ...

  3. git push 使用教程

    git push命令用于将本地分支的更新,推送到远程主机.它的格式与git pull命令相仿. $ git push <远程主机名> <本地分支名>:<远程分支名> ...

  4. intelij idea相关笔记--持续更新

    一.快捷键: Ctrl+F 文件内查找 Ctrl+Shift+F 全局查找 Ctrl+Shift+N 查找文件 Ctrl+Alt+← 返回上一步 Ctrl+Alt+→ 返回下一步 二.编译相关: 如果 ...

  5. LEMP (LNMP) Stack-5.4.16 (OpenLogic CentOS 7.2)

    LEMP (LNMP) Stack-5.4.16 (OpenLogic CentOS 7.2) 平台: CentOS 类型: 虚拟机镜像 软件包: mariadb-5.5.47 nginx-1.6.3 ...

  6. LeetCode Missing Number (简单题)

    题意: 给一个含有n个整数的数组,数组中的元素应该是0-n.现在缺了其中某1个,找出缺少的那个整数? 思路: 0-n的总和是可以直接计算的,而缺少的那个就是sum减去数组的和. int missing ...

  7. LeetCode Excel Sheet Column Number 表列数

    题意:天啊!我竟然看不懂题意,还去翻别人的代码才懂!给定一个字符串,求该字符串二十六进制的总值. 思路:'A'~'Z'就是1到26,"AA"=26+1=27,"BA&qu ...

  8. 【微软大法好】VS Tools for AI全攻略(3):低配置虚拟机也能玩转深度学习,无需NC/NV系列

    接着上文,现在我们需要一种穷人的方法来搭建好Azure虚拟机. 思路很简单,因为AI组件的原理其实是传送了script文件和命令上去,那么我们这个虚拟机只要做好了所有的配置,那么我们就可以将它当作深度 ...

  9. Win7系统如何设置FTP详细过程

    1.安装FTP组件 点击:控制面板—>程序和功能—>打开或关闭Windows功能.勾选“FTP服务器”及“FTP服务”“FTP扩展性”,点击“确定”,安装FTP组件. 2.添加FTP站点 ...

  10. SAP Netweaver的负载均衡消息服务器 vs CloudFoundry的App Router

    Message server for ABAP Netweaver SAP传统应用经典的三层架构: 起到负载均衡的消息服务器(Message Server)在图中没有得到体现.然后,消息服务器在我们每 ...