Bitmap图片缓存处理大全

处理Bitmap图片缓存

android 处理位图有两种常用的方式:

  1. 采用软引用来处理位图。
  2. lruCache机制来处理位图。

原因

每当碰到一些大图片的时候,我们如果不对图片进行处理就会报OOM异常,
这个问题曾经让我觉得很烦恼,后来终于得到了解决,
那么现在就让我和大家一起分享一下吧。
这篇博文要讲的图片缓存机制,我接触到的有两钟,一种是软引用,另一种是内存缓存技术。
先来看下两者的使用方式,再来作比较。
除了加载图片时要用到缓存处理,还有一个比较重要的步骤要做,就是要先压缩图片。

//至于要压缩到什么状态就要看自己当时的处境了,压缩图片的时候既要达到一个小的值,又不能让其模糊,更不能拉伸图片。

            /**
             * 加载内存卡图片
             */
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true; // 设置了此属性一定要记得将值设置为false
            Bitmap bitmap = null;
            bitmap = BitmapFactory.decodeFile(url, options);
            int be = (int) ((options.outHeight > options.outWidth ? options.outHeight / 150
                    : options.outWidth / 200));
            if (be <= 0) // 判断200是否超过原始图片高度
                be = 1; // 如果超过,则不进行缩放
            options.inSampleSize = be;
            options.inPreferredConfig = Bitmap.Config.ARGB_4444;
            options.inPurgeable = true;
            options.inInputShareable = true;
            options.inJustDecodeBounds = false;
            try {
                bitmap = BitmapFactory.decodeFile(url, options);
            } catch (OutOfMemoryError e) {
                System.gc();
                Log.e(TAG, "OutOfMemoryError");
            }

软引用


只要有足够的内存,就一直保持对象,直到发现内存吃紧且没有Strong Ref时才回收对象。
我们可以这样定义:map里面的键是用来放图片地址的,既可以是网络上的图片地址,也可以SDcard上的图片地址,
map里面的值里面放的是持有软引用的Bitmap,当然如果你要放Drawable,那也是可以的。`

private Map<String, SoftReference<Bitmap>> imageMap 
                                           = new HashMap<String, SoftReference<Bitmap>>();

`
接下来就让我再介绍一下如何具体加载图片:
步骤:(1)先通过URL查看缓存中是否有图片,如果有,则直接去缓存中取得。
如果没有,就开线程重新去网上下载。
(2)下载完了之后,就把图片放在缓存里面,方便下次可以直接从缓存中取得。



    public Bitmap loadBitmap(final String imageUrl,final ImageCallBack imageCallBack) {
            SoftReference<Bitmap> reference = imageMap.get(imageUrl);
            if(reference != null) {
                if(reference.get() != null) {
                    return reference.get();
                }
            }
            final Handler handler = new Handler() {
                public void handleMessage(final android.os.Message msg) {
                    //加入到缓存中
                    Bitmap bitmap = (Bitmap)msg.obj;
                    imageMap.put(imageUrl, new SoftReference<Bitmap>(bitmap));
                    if(imageCallBack != null) {
                        imageCallBack.getBitmap(bitmap);
                    }
                }
            };
            new Thread(){
                public void run() {
                    Message message = handler.obtainMessage();
                    message.obj = downloadBitmap(imageUrl);
                    handler.sendMessage(message);
                }
            }.start();
            return null ;
        }

        // 从网上下载图片
        private Bitmap downloadBitmap (String imageUrl) {
            Bitmap bitmap = null;
            try {
                bitmap = BitmapFactory.decodeStream(new URL(imageUrl).openStream());
                return bitmap ;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }

        public interface ImageCallBack{
            void getBitmap(Bitmap bitmap);
        }

内存缓存技术


另外一种图片缓存的方式就是内存缓存技术。在Android中,有一个叫做LruCache类专门用来做图片缓存处理的。
它有一个特点,当缓存的图片达到了预先设定的值的时候,那么近期使用次数最少的图片就会被回收掉。
步骤:(1)要先设置缓存图片的内存大小,我这里设置为手机内存的1/8,
手机内存的获取方式:int MAXMEMONRY = (int) (Runtime.getRuntime() .maxMemory() / 1024);
(2)LruCache里面的键值对分别是URL和对应的图片
(3)重写了一个叫做sizeOf的方法,返回的是图片数量。



    private LruCache<String, Bitmap> mMemoryCache;
    private LruCacheUtils() {
            if (mMemoryCache == null)
                mMemoryCache = new LruCache<String, Bitmap>(
                        MAXMEMONRY / 8) {
                    @Override
                    protected int sizeOf(String key, Bitmap bitmap) {
                        // 重写此方法来衡量每张图片的大小,默认返回图片数量。
                        return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
                    }

                    @Override
                    protected void entryRemoved(boolean evicted, String key,
                            Bitmap oldValue, Bitmap newValue) {
                        Log.v("tag", "hard cache is full , push to soft cache");

                    }
                };
        }

(4)下面的方法分别是清空缓存、添加图片到缓存、从缓存中取得图片、从缓存中移除。
移除和清除缓存是必须要做的事,因为图片缓存处理不当就会报内存溢出,所以一定要引起注意。



    public void clearCache() {
            if (mMemoryCache != null) {
                if (mMemoryCache.size() > 0) {
                    Log.d("CacheUtils",
                            "mMemoryCache.size() " + mMemoryCache.size());
                    mMemoryCache.evictAll();
                    Log.d("CacheUtils", "mMemoryCache.size()" + mMemoryCache.size());
                }
                mMemoryCache = null;
            }
        }

        public synchronized void addBitmapToMemoryCache(String key, Bitmap bitmap) {
            if (mMemoryCache.get(key) == null) {
                if (key != null && bitmap != null)
                    mMemoryCache.put(key, bitmap);
            } else
                Log.w(TAG, "the res is aready exits");
        }

        public synchronized Bitmap getBitmapFromMemCache(String key) {
            Bitmap bm = mMemoryCache.get(key);
            if (key != null) {
                return bm;
            }
            return null;
        }

        /**
         * 移除缓存
         *
         * @param key
         */
        public synchronized void removeImageCache(String key) {
            if (key != null) {
                if (mMemoryCache != null) {
                    Bitmap bm = mMemoryCache.remove(key);
                    if (bm != null)
                        bm.recycle();
                }
            }
        }

4、两者的比较
说到这里,我觉得有必要来进行一下比较了。
网上有很多人使用软引用加载图片的多 ,但是现在已经不再推荐使用这种方式了,
(1)因为从 Android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,
这让软引用和弱引用变得不再可靠。
(2)另外,Android 3.0 (API Level 11)中,图片的数据会存储在本地的内存当中,
因而无法用一种可预见的方式将其释放,这就有潜在的风险造成应用程序的内存溢出并崩溃,
所以我这里用得是LruCache来缓存图片,当存储Image的大小大于LruCache设定的值,系统自动释放内存,
这个类是3.1版本中提供的,如果你是在更早的Android版本中开发,则需要导入android-support-v4的jar包。

另一种实现软引用的方式


public class BitmapManager {

    /** 用于Cache内容的存储。 */
    private static HashMap<String, BitmapRef> mBitmapRefMap = new HashMap<String, BitmapRef>();
    /** 垃圾Reference的队列。 */
    private static ReferenceQueue<Bitmap> mGcQueue = new ReferenceQueue<Bitmap>();

    /**
     * 继承SoftReference,使得每一个实例都具有可识别的标识。<br />
     * 并且该标识与其在HashMap内的key相同。
     * @author Zhoucj
     */
    private static class BitmapRef extends SoftReference<Bitmap> {
        private String mKey = "";

        public BitmapRef(Bitmap em, ReferenceQueue<Bitmap> mGcQueue, String bitmapName) {
            super(em, mGcQueue);
            mKey = bitmapName;
        }
    }

    /**
     * 根据图片名获取Bitmap。
     * @param bitmapName 资源名。
     * @return Bitmap 缓存中的位图。
     */
    public synchronized static final Bitmap getCacheBitmap(String bitmapName) {
        Bitmap bitmap = null;
        if (mBitmapRefMap.containsKey(bitmapName)) {
            BitmapRef ref = (BitmapRef) mBitmapRefMap.get(bitmapName);
            bitmap = (Bitmap) ref.get();
        }
        return bitmap;
    }

    /**
     * 根据图片名移除Bitmap。
     * @param bitmapName 资源名。
     * @return Bitmap 被移出的图片。
     */
    public synchronized static final Bitmap removeCacheBitmap(String bitmapName) {
        Bitmap bitmap = null;
        if (mBitmapRefMap.containsKey(bitmapName)) {
            mBitmapRefMap.remove(bitmapName);
        }

        return bitmap;
    }

    /**
     * 缓存图片。
     * @param bitmapName 图片名字。
     * @param em 图片实例。
     */
    private synchronized static void cacheBitmap(String bitmapName, Bitmap em) {
        cleanCache();
        BitmapRef ref = new BitmapRef(em, mGcQueue, bitmapName);
        mBitmapRefMap.put(bitmapName, ref);
    }

    /**
     * 清除那些软引用的Employee对象、已经被回收的EmployeeRef对象。
     */
    private synchronized static void cleanCache() {
        BitmapRef ref = null;
        while ((ref = (BitmapRef) mGcQueue.poll()) != null) {
            mBitmapRefMap.remove(ref.mKey);
        }
    }

    /**
     * 清除Cache内的全部内容。
     */
    public synchronized void clearCache() {
        cleanCache();
        mBitmapRefMap.clear();
        System.gc();
        System.runFinalization();
    }


对图片的各种处理


/**
     * 由byte[] 数据获取 Bitmap。
     * @param bitmapName 位图名字。
     * @param buffer 资源数据。
     * @return Bitmap 获取到的位图。
     * @see #createBitmap(String, byte[], float, float)
     */
    public static final Bitmap createBitmap(String bitmapName, byte[] buffer) {
        if (buffer != null && buffer.length > 0) {
            try {
                BitmapFactory.Options opt = new BitmapFactory.Options();
                opt.inPreferredConfig = Bitmap.Config.RGB_565;
                opt.inPurgeable = true;
                opt.inInputShareable = true;

                Bitmap bitmap = BitmapFactory.decodeByteArray(buffer, 0, buffer.length, opt);
                if (bitmapName != null && bitmap != null) {
                    // 保存对这个新建图片的软引用
                    cacheBitmap(bitmapName, bitmap);
                }
                return bitmap;
            } catch (Exception e) {
                Utils.printException(e);
            }
        }
        return null;
    }
/**
     * 得到灰度图片。
     * @param bitmapName 图片名字。
     * @param bmpOriginal 原始图片。
     * @return 灰度图。
     */
    public static Bitmap getGrayBitmap(String bitmapName, Bitmap bmpOriginal) {
        Bitmap grayBitmap;
        String grayName = bitmapName + "_gray";
        grayBitmap = getCacheBitmap(grayName);// 先从缓存中去取
        if (grayBitmap == null) {
            grayBitmap = toGrayBitmap(bmpOriginal);
            cacheBitmap(grayName.toString(), grayBitmap);// 缓存裁剪后的图片
        }
        return grayBitmap;
    }

    /**
     * 图片去色,返回灰度图片。
     * @param bmpOriginal 传入的图。
     * @return 去色后的图片。
     */
    private static Bitmap toGrayBitmap(Bitmap bmpOriginal) {
        if (null == bmpOriginal) {
            return null;
        }
        int width, height;
        height = bmpOriginal.getHeight();
        width = bmpOriginal.getWidth();
        Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bmpGrayscale);
        Paint paint = new Paint();
        ColorMatrix cm = new ColorMatrix();
        cm.setSaturation(0);
        ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
        paint.setColorFilter(f);
        c.drawBitmap(bmpOriginal, 0, 0, paint);

        return bmpGrayscale;
    }
/**
     * 得到剪切为圆角的图片。
     * @param bitmapName 图片名字。
     * @param bmpOriginal 原始图片。
     * @param width 目标宽度。
     * @param height 目标高度。
     * @param borderRadius int[4] 圆角弧度。<br />
     *            圆角弧度值的顺序为从左上角顺时针旋转。
     * @return Bitmap 剪切为圆角的图片。
     */
    public static Bitmap getRoundBitmap(String bitmapName, Bitmap bmpOriginal, final int width, final int height, final int[] borderRadius) {
        Bitmap roundBitmap;
        StringBuffer roundName = new StringBuffer(bitmapName);
        roundName.append("_").append(width).append("_").append(height);
        if (borderRadius != null) {
            for (int i = 0; i < borderRadius.length; i++) {
                roundName.append("_").append(borderRadius[i]);
            }
        }
        roundBitmap = getCacheBitmap(roundName.toString());// 先从缓存中去取
        if (roundBitmap == null) {
            roundBitmap = toRoundBitmap(bmpOriginal, width, height, borderRadius);
            cacheBitmap(roundName.toString(), roundBitmap);// 缓存裁剪后的图片
        } else {
            Utils.printOutToConsole("BitmapManager use cache----------------");
        }
        return roundBitmap;
    }

    /**
     * 剪切图片为圆角。
     * @param bmpOriginal 原始图片。
     * @param width 目标宽度。
     * @param height 目标高度。
     * @param borderRadius 圆角弧度。
     * @return 修正为圆角的图片。
     */
    private static Bitmap toRoundBitmap(Bitmap bmpOriginal, final int width, final int height, final int[] borderRadius) {
        if (bmpOriginal == null || width <= 0 || height <= 0) {
            return null;
        }
        Bitmap scaleBitmap = Bitmap.createScaledBitmap(bmpOriginal, width, height, true);
        Bitmap output = Bitmap.createBitmap(width, height, Config.ARGB_8888);
        if (borderRadius != null && borderRadius.length == 4) {
            int sum = 0;
            for (int i = 0; i < borderRadius.length; i++) {
                if (borderRadius[i] < 0) {
                    return null;
                }
                sum += borderRadius[i];
            }
            if (sum == 0) {
                return scaleBitmap;
            }

            Canvas canvas = new Canvas(output);
            final Paint paint = new Paint();
            paint.setColor(Color.RED);
            final Rect rect = new Rect(0, 0, width, height);
            paint.setAntiAlias(true);

            int mMaxRadiusX = width / 2;
            int mMaxRadiusY = height / 2;
            int topLeftRadiusX = Math.min(mMaxRadiusY, borderRadius[0]);
            int topLeftRadiusY = Math.min(mMaxRadiusY, borderRadius[0]);
            int topRightRadiusX = Math.min(mMaxRadiusX, borderRadius[1]);
            int topRightRadiusY = Math.min(mMaxRadiusY, borderRadius[1]);
            int bottomRightRadiusX = Math.min(mMaxRadiusX, borderRadius[2]);
            int bottomRightRadiusY = Math.min(mMaxRadiusY, borderRadius[2]);
            int bottomLeftRadiusX = Math.min(mMaxRadiusX, borderRadius[3]);
            int bottomLeftRadiusY = Math.min(mMaxRadiusY, borderRadius[3]);

            RectF arc1 = new RectF(0, 0, topLeftRadiusX * 2, topLeftRadiusY * 2);
            RectF rect1 = new RectF(0, 0, topLeftRadiusX, topLeftRadiusY);
            RectF arc2 = new RectF(width - topRightRadiusX * 2, 0, width, topRightRadiusY * 2);
            RectF rect2 = new RectF(width - topRightRadiusX, 0, width, topRightRadiusY);
            RectF arc3 = new RectF(width - bottomRightRadiusX * 2, height - bottomRightRadiusY * 2, width, height);
            RectF rect3 = new RectF(width - bottomRightRadiusX, height - bottomRightRadiusY, width, height);
            RectF arc4 = new RectF(0, height - bottomLeftRadiusY * 2, bottomLeftRadiusX * 2, height);
            RectF rect4 = new RectF(0, height - bottomLeftRadiusY, bottomLeftRadiusX, height);

            canvas.drawRect(rect1, paint);
            canvas.drawRect(rect2, paint);
            canvas.drawRect(rect3, paint);
            canvas.drawRect(rect4, paint);
            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
            canvas.drawArc(arc1, 180, 90, true, paint);
            canvas.drawArc(arc2, 270, 90, true, paint);
            canvas.drawArc(arc3, 0, 90, true, paint);
            canvas.drawArc(arc4, 90, 90, true, paint);
            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
            canvas.drawRect(0, 0, width, height, paint);
            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
            canvas.drawBitmap(scaleBitmap, rect, rect, paint);
        }
        scaleBitmap.recycle(); // 回收图片所占的内存
        scaleBitmap = null;

        return output;
    }
/**
     * 为图片增加圆角边框。
     * @param bitmap 原始图片。
     * @param borderColor 边框线颜色。
     * @param borderWidth 边框线宽度。
     * @param borderRadius 边框圆角。
     * @return 增加了圆角边框的图片。
     */
    public static Drawable addBorder(Bitmap bitmap, final int borderColor, final float[] borderWidths, final float[] borderRadius) {

        if (bitmap == null) {
            return null;
        }

        int sum = 0;
        for (int i = 0; i < borderWidths.length; i++) {
            if (borderWidths[i] < 0) {
                return new BitmapDrawable(bitmap);
            }
            sum += borderWidths[i];
        }
        if (sum == 0) {
            return new BitmapDrawable(bitmap);
        }

        Drawable drawable = new BitmapDrawable(bitmap) {
            @Override
            public void draw(Canvas canvas) {
                super.draw(canvas);
                BorderHandler.drawBorder(canvas, borderColor, borderWidths, borderRadius);
            }
        };
        return drawable;
    }

    /**
     * 判断字符串是否以.png结尾,如果不是则加上该后缀。
     * @param name 图片目录/名称。
     * @return 以.png结尾的图片信息。
     */
    public static final String getPNGName(String name) {
        name = name.trim();
        if (!name.endsWith(".png")) {
            if (name.indexOf(".") != -1) {
                name = name.substring(0, name.indexOf("."));
            }
            name = name.concat(".png");
        }
        return name;
    }

    /**
     * 根据资源id获取Bitmap。
     * @param resourceID 资源id。
     * @param context Context 应用程序上下文。
     * @return Bitmap 资源位图。
     * @see #createBitmap(String, byte[])
     * @see #createBitmap(String, byte[], float, float)
     */
    public static final Bitmap getBitmap(int resourceID, Context context) {
        BitmapFactory.Options opt = new BitmapFactory.Options();
        opt.inPreferredConfig = Bitmap.Config.RGB_565;
        opt.inPurgeable = true;
        opt.inInputShareable = true;
        InputStream is = context.getResources().openRawResource(resourceID);

        Bitmap img = BitmapFactory.decodeStream(is, null, opt);
        try {
            is.close();
        } catch (IOException e) {
            Utils.printException(e);
        }
        return img;
    }

    /**
     * 获取指定宽高的图片。
     * @param resourceID 资源id。
     * @param context Context 应用程序上下文。
     * @param targetWidth 返回图片的宽度。
     * @param targetHeight 返回图片的高度。
     * @return Bitmap 结果位图。
     * @see #createBitmap(String, byte[])
     * @see #createBitmap(String, byte[], float, float)
     */
    public static final Bitmap getBitmap(int resourceID, Context context, float targetWidth, float targetHeight) {
        BitmapFactory.Options opt = new BitmapFactory.Options();
        opt.inJustDecodeBounds = true;
        InputStream is = context.getResources().openRawResource(resourceID);

        Bitmap img = BitmapFactory.decodeStream(is, null, opt);
        opt.inJustDecodeBounds = false;
        int be = (int) (opt.outHeight / targetHeight);
        if (be <= 0) {
            be = 1;
        }
        opt.inSampleSize = be;
        // 操作系统版本号大于4.4时,需要重新获取下流,否则不能正确获取图片。
        try {
            float osVersion = Float.parseFloat(Utils.getVersion().substring(0, 3));
            if (osVersion >= 4.4f) {
                is = context.getResources().openRawResource(resourceID);
            }
        } catch (Exception e) {
        }
        img = BitmapFactory.decodeStream(is, null, opt);
        try {
            is.close();
        } catch (IOException e) {
            Utils.printException(e);
        }
        return img;
    }
----------------------通过Drawable得到Bitmap-----------------

BitmapDrawable bd = (BitmapDrawable) drawable;
bitmap = bd.getBitmap();

canvas.drawRect(new Rect(left,top,right,bottom),mPaint);  绘制矩形(左,上,右,下) 矩形的对角线
canvas.drawText('3',x,y,Paint);--画笔画文字
x默认是‘3’这个字符的左边在屏幕的位置,如果设置了paint.setTextAlign(Paint.Align.CENTER);那就是字符的中心,y是指定这个字符baseline在屏幕上的位置

部分内容转自:http://blog.chinaunix.net/uid-26930580-id-4138306.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值