在Android2.3.3(APILevel10)以及之前,Bitmap的backingpixel数据存储在nativememory,与Bitmap本身是分开的,Bitmap本身存储在dalvikheap中。导致其pixel数据不能判断是否还需要使用,不能及时释放,容易引起OOM错误。从Android3.0(API11)开始,pixel数据与Bitmap一起存储在Dalvikheap中。

结论:

如何处理图片来避免OOM异常:

1.在Android2.3.3以及之前,建议使用Bitmap.recycle()方法,及时释放资源。

2.设置Options.inPreferredConfig值来降低内存消耗 //如把默认值ARGB_8888改为RGB_565,节约一半内存

3.设置Options.inSampleSize对大图片进行压缩

4.设置Options.inPurgeable和inInputShareable:让系统能及时回收内存。

1)inPurgeable:设置为True时,表示系统内存不足时可以被回收,设置为False时,表示不能被回收。

2)inInputShareable:设置是否深拷贝,与inPurgeable结合使用,inPurgeable为false时,该参数无意义True:shareareferencetotheinputdata(inputStream,array,etc)。False:adeepcopy。

5.使用decodeStream代替其他decodeResource,setImageResource,setImageBitmap等方法:

//decodeStream直接调用JNI>>nativeDecodeAsset()来完成decode,无需再使用java层的createBitmap,也不使用java空间进行分辨率适配,虽节省dalvik内存,但需要在hdpi和mdpi,ldpi中配置相应的图片资源,否则在不同分辨率机器上都是同样大小(像素点数量)。其他方法如setImageBitmap、setImageResource、BitmapFactory.decodeResource在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存。

在android机器上处理图片时候,经常会遇到各种各样的情况导致out of memory错误,这种情况比一般的exception难处理的多,因为你要为自己所使用的每一份内存负责。遇到这种情况时候,不能慌,只能慢慢抽茧剥丝一点点处理。

在这次处理过程中,我整理了一下几个处理的思路,应该可以减轻部分的oom症状。

1、从读入内存入手。

在读入图片时候可以给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做个说明。

  1. 如果

    inPurgeable

    设为True的话表示使用BitmapFactory创建的Bitmap

    用于存储Pixel的内存空间在系统内存不足时可以被回收,在应用需要再次访问Bitmap的Pixel时(如绘制Bitmap或是调用getPixel),系统会再次调用BitmapFactory

    decoder重新生成Bitmap的Pixel数组。为了能够重新解码图像,bitmap要能够访问存储Bitmap的原始数据。

  2. 在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了。



小结:为了能够让系统能够处理我们的图片,这里费尽心机的压缩读入的图片尺寸,当然还是需要在保证需求的前提下的。

2、从使用内存入手

i、及时删除不用的bitmap。想要尽量少的使用用内存,要保证在每个bitmap不使用的时候及时mBitmap.recycle();System.gc();

这个时候要保证这个bitmap是不会再被使用的,也就是新的副本已经被createBitmap或者copy出来了。不然的话,你随便的recycle会导致当前正在使用的图片也会被回收,页面上就没图片了,严重的还会导致系统在调用图片时候崩溃。所以之一部需要很细致。

后面的这个system.gc()只是通知虚拟机可以回收了,但是虚拟机什么时候回收还不一定,所以不要寄希望于这个东西会立马清出你需要的内存。

这里可以通过DDMS里面查看应用的heap使用情况来确认你的bitmap处理是否正常。

3、从图片存储和重复利用入手

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();

}

这个都有注释了,应该都可以看得懂。文件方面就和普通的一样了。

4、从页面优化入手

i、使用viewstub替换掉经常使用的view.visible,viewstub在不显示的时候会不占用内存,另外一个会占用内存。这样可以减少图片占用的内存使用,同样适用于其他的内存消耗。

忘记从哪位高人转载的了,如有侵权请告知

更多相关文章

  1. Android内存溢出的解决方法(VMRuntime.getRuntime().setMini...
  2. Android(安卓)Studio 检测内存泄漏与解决方法
  3. AndroidQuery 开源项目
  4. Android给图片加文字和图片水印
  5. android拍照获得图片及获得图片后剪切设置到ImageView
  6. Android(安卓)sqlite数据库简单使用(创建和插入,查询数据)
  7. android 视频图片轮播
  8. Android内存优化总结
  9. Android(安卓)Dalvikvm 内存管理理解

随机推荐

  1. MySQL实现字符串的拼接,截取,替换,查找位
  2. mysql 多个字段拼接的实例详解
  3. MySQL存储过程的查询命令介绍
  4. 解决Mysql报Invalid default value for &
  5. mysql中 datatime与timestamp的区别说明
  6. MySQL 8.0新特性之隐藏字段的深入讲解
  7. MySQL中Like概念及用法讲解
  8. 解决windows service 2012阿里云服务器在
  9. 使用Visual Studio Code连接MySql数据库
  10. mysql添加备注信息的实现