在Android 2.3.3(API Level 10)以及以前,Bitmap的backing pixel 数据存储在native memory, 与Bitmap自己是分开的,Bitmap自己存储在dalvik heap 中。致使其pixel数据不能判断是否还须要使用,不能及时释放,容易引发OOM错误。 从Android 3.0(API 11)开始,pixel数据与Bitmap一块儿存储在Dalvik heap中。java
结论:android
如何处理图片来避免OOM异常:
算法
1.在Android 2.3.3以及以前,建议使用Bitmap.recycle()方法,及时释放资源。数组
2.设置Options.inPreferredConfig值来下降内存消耗 //如把默认值ARGB_8888改成RGB_565,节约一半内存缓存
3.设置Options.inSampleSize 对大图片进行压缩
ide
4.设置Options.inPurgeable和inInputShareable:让系统能及时回收内存。
优化
1)inPurgeable:设置为True时,表示系统内存不足时能够被回 收,设置为False时,表示不能被回收。url
2)inInputShareable:设置是否深拷贝,与inPurgeable结合使用,inPurgeable为false时,该参数无心义True: share a reference to the input data(inputStream, array,etc) 。 False :a deep copy。spa
5.使用decodeStream代替其余decodeResource,setImageResource,setImageBitmap等方法:code
//decodeStream直接调用 JNI>>nativeDecodeAsset()来完成decode,无需再使用java层的createBitmap,也不使用java空间进行分辨率适配,虽节省dalvik内存,但须要在hdpi和mdpi,ldpi中配置相应的 图片资源,不然在不一样分辨率机器上都是一样大小(像素点数量)。其余方法如setImageBitmap、setImageResource、 BitmapFactory.decodeResource在完成decode后,最终都是经过java层的 createBitmap来完成的,须要消耗更多内存。
在android机器上处理图片时候,常常会遇到各类各样的状况致使out of memory错误,这种状况比通常的exception难处理的多,由于你要为本身所使用的每一分内存负责。遇到这种状况时候,不能慌,只能慢慢抽茧剥丝一点点处理。
在此次处理过程当中,我整理了一下几个处理的思路,应该能够减轻部分的oom症状。
一、从读入内存入手。
在读入图片时候能够给BitmapFactory设置各类参数,使得咱们可以对读入的图片尺寸作出控制。
FileInputStream fis;
fis = new FileInputStream(path);
int size = fis.available();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
options.inPurgeable = true;
options.inInputShareable = true;
options.inPreferredConfig = Bitmap.Config.RGB_565;
if (size > 409600 * 4)
{
options.inSampleSize = (int)Math.sqrt(size / 409600);
bitmap = BitmapFactory.decodeStream(fis, null, options);
}
else
{
bitmap = BitmapFactory.decodeStream(fis, null, options);
}
步骤:
i、先获取图片大小,而后根据图片大小来设置不一样的缩放比例,因此使用了FileInputStream,能够获取图片的大小。
ii、设置这两个东西inPurgeable 和inInputShareable 。这里对inPurgeable作个说明。
若是
inPurgeable
设为True的话表示使用BitmapFactory建立的Bitmap
用于存储Pixel的内存空间在系统内存不足时能够被回收, 在应用须要再次访问Bitmap的Pixel时(如绘制Bitmap或是调用getPixel),系统会再次调用BitmapFactory
decoder从新生成Bitmap的Pixel数组。 为了可以从新解码图像,bitmap要可以访问存储Bitmap的原始数据。
在inPurgeable为false时表示建立的Bitmap的Pixel内存空间不能被回收,
这样BitmapFactory在不停decodeByteArray建立新的Bitmap对象,不一样设备的内存不一样,所以可以同时建立的Bitmap个数可能有所不一样, 200个bitmap足以使大部分的设备从新OutOfMemory错误。
当isPurgable设为true时,系统中内存不足时, 能够回收部分Bitmap占据的内存空间,这时通常不会出现OutOfMemory
错误。
因此设置inPurgeable = true是颇有必要的。这个inInputShareable是和inPurgeable 搭配使用的。
iii、设置读取色彩。
android中有4中色彩。
ALPHA_8:每一个像素占用1byte内存
ARGB_4444:每一个像素占用2byte内存
ARGB_8888:每一个像素占用4byte内存
RGB_565:每一个像素占用2byte内存
Android默认的颜色模式为ARGB_8888,因此咱们采用inPreferredConfig = Bitmap.Config.RGB_565;方式读取,能够减小内存消耗,失真在手机查看的状况下都可以忍受。
iiii、读取缩略图进入内存。由于大图片在手机上处理不须要彻底展现,因此不须要加载所有。我这里使用了1.6m做为压缩的压缩的基本尺寸,低于这个就不压缩直接读取。使用这个尺寸由于须要图片大小至少为640*640,对于图片大小没有限制的能够采用更小的尺寸做为压缩的基本尺寸。options.inSampleSize这个参数能够控制读取的压缩比例。官方推荐的是使用2的幂次方比例,我这里也只是使用了普通的压缩比例。这个须要注意的是这个压缩是针对宽和高的压缩,因此对大小是压缩比例的平方。
另外,这里吐槽一下,开始使用的是0.8m,结果小米的自带相机优化,使得按照200k大小取缩略图后宽高居然不够640*640了,其余的手机都是ok的,只能改成1.6m了。
小结:为了可以让系统可以处理咱们的图片,这里费尽心机的压缩读入的图片尺寸,固然仍是须要在保证需求的前提下的。
二、从使用内存入手
i、及时删除不用的bitmap。想要尽可能少的使用用内存,要保证在每一个bitmap不使用的时候及时mBitmap.recycle();System.gc();
这个时候要保证这个bitmap是不会再被使用的,也就是新的副本已经被createBitmap或者copy出来了。否则的话,你随便的recycle会致使当前正在使用的图片也会被回收,页面上就没图片了,严重的还会致使系统在调用图片时候崩溃。因此之一部须要很细致。
后面的这个system.gc()只是通知虚拟机能够回收了,可是虚拟机何时回收还不必定,因此不要寄但愿于这个东西会立马清出你须要的内存。
这里能够经过DDMS里面查看应用的heap使用状况来确认你的bitmap处理是否正常。
三、从图片存储和重复利用入手
i、使用内存缓存和文件缓存处理。由于从内存读取图片会比较快,因此为了更大限度使用内存,使用两层缓存。硬引用缓存不会轻易被回收,用来保存经常使用数据,不经常使用的转入软引用缓存。
private static final int SOFT_CACHE_SIZE = 15; //软引用缓存容量
private static LruCache mLruCache; //硬引用缓存
private static LinkedHashMap> mSoftCache; //软引用缓存
public ImageMemoryCache(Context context) {
int memClass = ((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();
int cacheSize = 1024 * 1024 * memClass / 4; //硬引用缓存容量,为系统可用内存的1/4
mLruCache = new LruCache(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap value) {
if (value != null)
return value.getRowBytes() * value.getHeight();
else
return 0;
}
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
if (oldValue != null)
// 硬引用缓存容量满的时候,会根据LRU算法把最近没有被使用的图片转入此软引用缓存
mSoftCache.put(key, new SoftReference(oldValue));
}
};
mSoftCache = new LinkedHashMap>(SOFT_CACHE_SIZE, 0.75f, true) {
private static final long serialVersionUID = 6040103833179403725L;
@Override
protected boolean removeEldestEntry(Entry> eldest) {
if (size() > SOFT_CACHE_SIZE){
return true;
}
return false;
}
};
}
public Bitmap getBitmapFromCache(String url) {
if(url==null){
return null;
}
Bitmap bitmap;
//先从硬引用缓存中获取
synchronized (mLruCache) {
bitmap = mLruCache.get(url);
if (bitmap != null) {
//若是找到的话,把元素移到LinkedHashMap的最前面,从而保证在LRU算法中是最后被删除
mLruCache.remove(url);
mLruCache.put(url, bitmap);
return bitmap;
}
}
//若是硬引用缓存中找不到,到软引用缓存中找
synchronized (mSoftCache) {
SoftReference bitmapReference = mSoftCache.get(url);
if (bitmapReference != null) {
bitmap = bitmapReference.get();
if (bitmap != null) {
//将图片移回硬缓存
mLruCache.put(url, bitmap);
mSoftCache.remove(url);
return bitmap;
} else {
mSoftCache.remove(url);
}
}
}
return null;
}
public void addBitmapToCache(String url, Bitmap bitmap) {
if (bitmap != null) {
synchronized (mLruCache) {
mLruCache.put(url, bitmap);
}
}
}
public void clearCache() {
mSoftCache.clear();
}
这个都有注释了,应该均可以看得懂。文件方面就和普通的同样了。
四、从页面优化入手
i、使用viewstub替换掉常用的view.visible,viewstub在不显示的时候会不占用内存,另一个会占用内存。这样能够减小图片占用的内存使用,一样适用于其余的内存消耗。
忘记从哪位高人转载的了,若有侵权请告知