如何避免图片加载OOM

来源:互联网 发布:罗辑思维人工智能文章 编辑:程序博客网 时间:2024/06/04 10:56

在编写程序的时候,经常需要显示很多图片,当图片质量较高,尺寸和分辨率较大时,我们的程序可能吃不消!因为程序都有一定的内存大小限制,这就可能会造成OOM(内存溢出)。

那么,该如何解决这个问题呢?思路就是,在展示高分辨率的图片的时候,肯定会对其进行压缩,然后根据控件的大小调整。 

最基本的压缩方法:BitmapFactory

BitmapFactory提供了一个Options的方法,该方法里面包含了解析图片的相关参数,当我们第一次解析图片的时候因为不确定图片的大小,所以建议设置Options.InjustdecodeBounds=true,这样的话BitmapFactory.decodeResources(res,id,options)只会去解析图片的宽高和MiME类型,并不会为其分配内存,其中options.outwidth和option.height可以的到图片的宽高,知道宽高以后我们就可以对其进行缩放了,options.simpleSize这个属性可以按比例压缩图片,当然这个simpleSize需要经过计算,最后设置injustdecodebounds=false,就可以返回bitmap了

 /**     * @param res   getResources     * @param resID R.id.image     * @param w     期望的宽     * @param h     期望的高     */    public static Bitmap decodeImage(Resources res, int resID, int w, int h) {        BitmapFactory.Options options = new BitmapFactory.Options();        options.inJustDecodeBounds = true;        //第一次解析图片,不分配内存,只得到宽和高        BitmapFactory.decodeResource(res, resID, options);        options.inSampleSize = calaculateSampleSize(options, w, h);        options.inJustDecodeBounds=false;        //根据得到的宽高再次解析图片        return BitmapFactory.decodeResource(res,resID,options);    }    static int calaculateSampleSize(BitmapFactory.Options options, int w, int h) {        //图片原本的宽和高        int width = options.outWidth;        int height = options.outHeight;        int simpleSize = 1;        if (width > w || height > h) {            int widthRadio = Math.round((float) width / (float) w);            int heightRadio = Math.round((float) height / (float) h);            // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高            // 一定都会大于等于目标的宽和高。            simpleSize=widthRadio>heightRadio?heightRadio:widthRadio;        }        return simpleSize;    }

OK,这只是加载一张图片,那么问题来了,如果成百上千张图片怎么办呢?程序中我们经常使用listview,girdview,recycleView等联网加载大量图片,在不断滑动的时候会不断的去解析加载图片,结果可想而知,图片不断增加,最终导致OOM,有人会说,我把屏幕之外的图片进行释放,只显示屏幕当中的图片不就行了?听起来好像没错,的确可以减轻程序的内存压力,但是另外一个问题又出现了,我们不断的回收图片,那么重新加载的时候岂不是又消耗了内存、流量和时间?这里不得不提出一个新的概念:缓存

在androidV4包中提供了一个类:LruCache,这个类提供了一套算法,可以把最近使用的对象强引用存储在LinkedHashMap ,把最近最少使用的对象在缓存达到峰值时从内存中删除,那么如何使用呢?

private LruCache<String, Bitmap> mMemoryCache;@Overrideprotected void onCreate(Bundle savedInstanceState) {// 获取到可用内存的最大值,使用内存超出这个值会引起OutOfMemory异常。// LruCache通过构造函数传入缓存值,以KB为单位。int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);// 使用最大可用内存值的1/8作为缓存的大小。int cacheSize = maxMemory / 8;mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {@Overrideprotected int sizeOf(String key, Bitmap bitmap) {// 重写此方法来衡量每张图片的大小,默认返回图片数量。return bitmap.getByteCount() / 1024;}};}public void addBitmapToMemoryCache(String key, Bitmap bitmap) {if (getBitmapFromMemCache(key) == null) {mMemoryCache.put(key, bitmap);}}public Bitmap getBitmapFromMemCache(String key) {return mMemoryCache.get(key);}public void loadBitmap(int resId, ImageView imageView) {final String imageKey = String.valueOf(resId);final Bitmap bitmap = getBitmapFromMemCache(imageKey);if (bitmap != null) {imageView.setImageBitmap(bitmap);} else {imageView.setImageResource(R.drawable.image_placeholder);BitmapWorkerTask task = new BitmapWorkerTask(imageView);task.execute(resId);}}class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {// 在后台加载图片。@Overrideprotected Bitmap doInBackground(Integer... params) {final Bitmap bitmap =decodeImage(getResources(), params[0], 100, 100);

addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);return bitmap;}}

以上内容参考bolg:http://blog.csdn.net/guolin_blog/article/details/9316683



0 0