Android在加载大背景图或者大量图片时,常常致使内存溢出(Out of Memory Error),本文根据我处理这些问题的经历及其它开发者的经验,整理解决方案以下(部分代码及文字出处没法考证):
方案1、读取图片时注意方法的调用,适当压缩 尽可能不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,由于这些函数在完成decode后,最终都是经过java层的createBitmap来完成的,须要消耗更多内存。 所以,改用先经过BitmapFactory.decodeStream方法,建立出一个bitmap,再将其设为ImageView的 source,decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsset()来完成decode,无需再使用java层的createBitmap,从而节省了java层的空间。html
InputStream is = this.getResources().openRawResource(R.drawable.pic1);java
BitmapFactory.Options options = new BitmapFactory.Options();android
options.inJustDecodeBounds = false;算法
options.inSampleSize = 10; // width,hight设为原来的十分一app
Bitmap btp = BitmapFactory.decodeStream(is, null, options);函数
若是在读取时加上图片的Config参数,能够跟有效减小加载的内存,从而跟有效阻止抛out of Memory异常。性能
/**优化
* 以最省内存的方式读取本地资源的图片ui
* @param contextthis
* @param resId
* @return
*/
public static Bitmap readBitMap(Context context, int resId){
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
// 获取资源图片
InputStream is = context.getResources().openRawResource(resId);
return BitmapFactory.decodeStream(is, null, opt);
}
另外,decodeStream直接拿图片来读取字节码, 不会根据机器的各类分辨率来自动适应,使用了decodeStream以后,须要在hdpi和mdpi,ldpi中配置相应的图片资源, 不然在不一样分辨率机器上都是一样大小(像素点数量),显示出来的大小就不对了。
方案2、在适当的时候及时回收图片占用的内存 一般Activity或者Fragment在onStop/onDestroy时候就能够释放图片资源:
if(imageView != null && imageView.getDrawable() != null){
Bitmap oldBitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
imageView.setImageDrawable(null);
if(oldBitmap != null){
oldBitmap.recycle();
oldBitmap = null;
}
}
// Other code.
System.gc();
在释放资源时,须要注意释放的Bitmap或者相关的Drawable是否有被其它类引用。若是正常的调用,能够经过Bitmap.isRecycled()方法来判断是否有被标记回收;而若是是被UI线程的界面相关代码使用,就须要特别当心避免回收有可能被使用的资源,否则有可能抛出系统异常: E/AndroidRuntime: java.lang.IllegalArgumentException: Cannot draw recycled bitmaps 而且该异常没法有效捕捉并处理。
方案3、没必要要的时候避免图片的完整加载 只须要知道图片大小的情形下,能够不完整加载图片到内存。 在使用BitmapFactory压缩图片的时候,BitmapFactory.Options设置inJustDecodeBounds为true后,再使用decodeFile()等方法,能够在不分配空间状态下计算出图片的大小。示例:
BitmapFactory.Options opts = new BitmapFactory.Options();
// 设置inJustDecodeBounds为false
opts.inJustDecodeBounds = false;
// 使用decodeFile方法获得图片的宽和高
BitmapFactory.decodeFile(path, opts);
// 打印出图片的宽和高
Log.d("example", opts.outWidth + "," + opts.outHeight);
(ps:原理其实就是经过图片的头部信息读取图片的基本信息)
方案4、优化Dalvik虚拟机的堆内存分配 堆(HEAP)是VM中占用内存最多的部分,一般是动态分配的。堆的大小不是一成不变的,一般有一个分配机制来控制它的大小。好比初始的HEAP是4M大,当4M的空间被占用超过75%的时候,从新分配堆为8M大;当8M被占用超过75%,分配堆为16M大。倒过来,当16M的堆利用不足30%的时候,缩减它的大小为8M大。从新设置堆的大小,尤为是压缩,通常会涉及到内存的拷贝,因此变动堆的大小对效率有不良影响。 Heap Utilization是堆的利用率。当实际的利用率偏离这个百分比的时候,虚拟机会在GC的时候调整堆内存大小,让实际占用率向个百分比靠拢。使用 dalvik.system.VMRuntime类提供的setTargetHeapUtilization方法能够加强程序堆内存的处理效率。
private final static float TARGET_HEAP_UTILIZATION = 0.75f;
// 在程序onCreate时就能够调用
VMRuntime.getRuntime().setTargetHeapUtilization(TARGET_HEAP_UTILIZATION);
方案5、自定义堆(Heap)内存大小 对于一些Android项目,影响性能瓶颈的主要是Android本身内存管理机制问题,目前手机厂商对RAM都比较吝啬,对于软件的流畅性来讲RAM对性能的影响十分敏感,除了优化Dalvik虚拟机的堆内存分配外,咱们还能够强制定义本身软件的对内存大小,咱们使用Dalvik提供的 dalvik.system.VMRuntime类来设置最小堆内存为例:
private final static int CWJ_HEAP_SIZE = 6 * 1024 * 1024 ;
VMRuntime.getRuntime().setMinimumHeapSize(CWJ_HEAP_SIZE); // 设置最小heap内存为6MB大小。
可是上面方法仍是存在问题,函数setMinimumHeapSize其实只是改变了堆的下限值,它能够防止过于频繁的堆内存分配,当设置最小堆内存大小超过上限值(Max Heap Size)时仍然采用堆的上限值,对于内存不足没什么做用。
最后介绍一下图片占用进程的内存算法。android中处理图片的基础类是Bitmap,顾名思义,就是位图。占用内存的算法如:图片的width*height*Config。 若是Config设置为ARGB_8888,那么上面的Config就是4。一张480*320的图片占用的内存就是480*320*4 byte。 在默认状况下android进程的内存占用量为16M,由于Bitmap他除了java中持有数据外,底层C++的 skia图形库还会持有一个SKBitmap对象,所以通常图片占用内存推荐大小应该不超过8M。这个能够调整,编译源代码时能够设置参数。
参考资料:http://www.tuicool.com/articles/yemM7zf
方案六:在Manifest.xml文件里面的<application 里面添加Android:largeHeap="true"
简单粗暴。这种方法容许应用须要耗费手机不少的内存空间,但倒是最快捷的解决办法
https://www.jianshu.com/p/ab4a7e353076
若是一个无用对象(不须要再使用的对象)仍然被其余对象持有引用,形成该对象没法被系统回收,以至该对象在堆中所占用的内存单元没法被释放而形成内存空间浪费,这中状况就是内存泄露。
垃圾回收的机制主要是看对象是否有引用指向该对象。
java对象的引用包括:强(引用),弱(引用),软(引用),虚(引用)
https://www.cnblogs.com/huajiezh/p/5835618.html
我理解的对象的引用和垃圾回收:
1,GC回收对象时,一看有强引用指向了这个对象,你强,我不吃(不回收)。
2,GC回收对象时,一看有弱引用指向了这个对象,你弱,我吃(回收)。
3,GC回收对象时,一看有软引用指向了这个对象,看一下本身的内存,够用不吃,哎呀内存不够了,我吃。
4,虚引用在任什么时候候均可能被GC回收,。若是程序发现某个虚引用已经被加入到引用队列,那么就能够在所引用的对象的内存被回收以前采起必要的行动。