android图片加载处理
在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做个说明。
如果
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了。
小结:为了能够让系统能够处理我们的图片,这里费尽心机的压缩读入的图片尺寸,当然还是需要在保证需求的前提下的。
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在不显示的时候会不占用内存,另外一个会占用内存。这样可以减少图片占用的内存使用,同样适用于其他的内存消耗。
忘记从哪位高人转载的了,如有侵权请告知
更多相关文章
- Android内存溢出的解决方法(VMRuntime.getRuntime().setMini...
- Android(安卓)Studio 检测内存泄漏与解决方法
- AndroidQuery 开源项目
- Android给图片加文字和图片水印
- android拍照获得图片及获得图片后剪切设置到ImageView
- Android(安卓)sqlite数据库简单使用(创建和插入,查询数据)
- android 视频图片轮播
- Android内存优化总结
- Android(安卓)Dalvikvm 内存管理理解