android图片加载处理

在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作个说明。

  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了。



小结:为了可以让系统可以处理咱们的图片,这里费尽心机的压缩读入的图片尺寸,固然仍是须要在保证需求的前提下的。

二、从使用内存入手

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在不显示的时候会不占用内存,另一个会占用内存。这样能够减小图片占用的内存使用,一样适用于其余的内存消耗。

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

相关文章
相关标签/搜索