Android 图片如何高效加载与缓存 (4) —— 生成图片缩略图

之前我在读取大量本地图片的时候,是通过一个BitmapFactory.Options来缩放图片然后进行加载。虽然这样处理能快速地处理大堆图片,并且也能减少内存的消耗。但是这样也有坏处。

比如一张 4000*4000 的图片与一张 500*500 在同一个文件夹之下的时候,也通过这种方式进行读取就会产生某些图片解析度很低,有的图片解析度过于高,甚至产生了像素堆积,只有一部分图片是正常的样子。
如下图,可以见到右上角的图片分辨率太低而 右边中间的那第一张图片却分辨率过高。其他的图片倒是没有出现其他问题。 所以为了让绝大多数的图片都能正常地显示,我们需要调整加载策略!

Android 图片如何高效加载与缓存 (4) —— 生成图片缩略图_第1张图片

今天我采用了一个不错的方法,能很好地显示我所测试的所有图片的同时也能照顾到内存的使用。所以我就马上爬上来和大家分享分享 QwQ

先说加载策略,如果你看了之后有了想法,请务必自己先实现一次。说不定你的方法比我的更好呢?QAQ

  1. 获取图片的长宽尺寸
  2. 计算缩放倍数 , 直到处理后的长宽能满足某一需求
  3. 通过计算好的缩放倍数加载图片
  4. 通过长宽的长宽不同 , 来进行居中裁剪一个正方形区域
  5. 缓存裁剪后的图片 , 同时输出到 ImageView

说完了策略,接下来就是代码时间 !!!

    /* 获取图片缩略图任务 */
    private class GenerateImageThumbnail implements Callable<String>{

        private int WIDTH_LIMIT = 500; //宽度限制
        private int HEIGHT_LIMIT = 500;  //高度限制

        private int scaleTimes;  //缩放倍数
        private String path;  //文件路径
        private String tag;  //图片唯一TAG
        private ImageView imageView;  //要输出的ImageView
        private Bitmap bitmap;  //得到的Bitmap对象
        private boolean autoScale;  //是否自动缩放
        private HandleOnLoaded handleOnLoaded;  //图片后期处理接口

        public GenerateImageThumbnail(String path, String tag, ImageView imageView, int scaleTimes , boolean autoScale , HandleOnLoaded handleOnLoaded) {
            this.path = path;
            this.tag = tag;
            this.imageView = imageView;
            this.scaleTimes = scaleTimes;
            this.handleOnLoaded = handleOnLoaded;
            this.autoScale = autoScale;
        }

        @Override
        public String call() throws Exception {
            //先试着读取缓存图片

            bitmap = imageCacher.getCache(tag);
            if (bitmap != null){
            //如果存在缓存,则移除 tag , 然后输出
                Log.d("OCImageLoader", "[CACHED] Reduced size Width: "+bitmap.getWidth()+" Height: "+bitmap.getHeight());
                cacheExecutor.removeTag(tag);
                runOnUIThread(new Runnable() {
                    @Override
                    public void run() {
                        onCompleted(imageView , bitmap);
                    }
                });
                return null;
            }

            //没有缓存,则进行读取操作

            //先仅仅读取图片的大小数据
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            bitmap = BitmapFactory.decodeFile(path,options);

            int width , height , outWidth , outHeight;

            //原始数据
            outWidth = options.outWidth;
            outHeight = options.outHeight;

            //进行缩放计算
            if (autoScale){

                //自动缩放. 缩放到 长和宽都小于限制 并且大于10单位 为止

                scaleTimes = 2;
                while (true){
                    width = outWidth/scaleTimes;
                    height = outHeight/scaleTimes;
                    if (width < WIDTH_LIMIT && height < HEIGHT_LIMIT && width >= 10 && height >= 10){
                        break;
                    }else {
                        scaleTimes += 1;
                    }
                }

            }else {

                //手动缩放,直接应用缩放比例
                width = outWidth/scaleTimes;
                height = outHeight/scaleTimes;
            }

            options.inJustDecodeBounds = false;
            options.inSampleSize = (outHeight/height + outWidth/width) / 2;

            //获得缩放后的图像,以及它的尺寸
            bitmap = BitmapFactory.decodeFile(path,options);
            width = bitmap.getWidth();
            height = bitmap.getHeight();

            //计算裁剪区域,以边长最短的边做正方形,并居中裁剪
            if (height <= width){
                //宽比高长的情况 或 等长的情况下

                int startPoint = (width / 2)-(height / 2);
                bitmap = Bitmap.createBitmap(bitmap , startPoint , 0 ,height , height);
            }else {
                //高比宽长的情况下

                int startPoint = (height / 2)-(width / 2 );
                bitmap = Bitmap.createBitmap(bitmap , 0 , startPoint ,width , width);
            }

            //如果有需要后期处理缩略图,则进行处理. 否则跳过
            if (handleOnLoaded != null){
                bitmap = handleOnLoaded.reduce(bitmap,tag);
            }

            Log.d("OCImageLoader", "Generating thumbnail of :"+path);
            Log.d("OCImageLoader", "Original size Width: "+outWidth+" Height: "+outHeight);
            Log.d("OCImageLoader", "Reduced size Width: "+bitmap.getWidth()+" Height: "+bitmap.getHeight());

            //移除线程池的任务TAG
            cacheExecutor.removeTag(tag);

            if (bitmap != null){
                imageCacher.putCache(tag,bitmap);  //缓存图片到LRU以及本地文件
                runOnUIThread(new Runnable() {
                    @Override
                    public void run() {
                        onCompleted(imageView , bitmap);
                    }
                });
            }

            return null;
        }

        private void onCompleted(ImageView imageView , Bitmap bitmap){

            if (bitmap != null && imageView != null && imageView.getTag().equals(tag)){
                //图像的输出. 如果imageView是当初指定的那个才输出图像,否则跳过

                if (durationMillis > 0 ){
                //图像渐入渐出动画
                    Drawable prevDrawable = imageView.getDrawable();
                    if (prevDrawable == null) {
                        prevDrawable = new ColorDrawable(TRANSPARENT);
                    }
                    Drawable nextDrawable = new BitmapDrawable(imageView.getResources(), bitmap);
                    TransitionDrawable transitionDrawable = new TransitionDrawable(
                            new Drawable[] { prevDrawable, nextDrawable });
                    imageView.setImageDrawable(transitionDrawable);
                    transitionDrawable.startTransition(durationMillis);
                }else {
                    imageView.setImageBitmap(bitmap);
                }
            }

        }
    }

最后是调用的方法:

    /** * 获取本地图片的缩略图 * @param path 文件路径 * @param imageView 图片要显示的ImageView * @param scaleTimes 图片缩放倍数 * @param autoScale 是否自动缩放. 启用自动缩放将忽略 scaleTimes 参数 * @param handleOnLoaded 图片缩略图生成完毕后对缩略图进行处理的接口 */
    public void loadImageThumbnail(String path , ImageView imageView , int scaleTimes , boolean autoScale , HandleOnLoaded handleOnLoaded){
        File checkFile = new File(path);
        if ( checkFile.isFile() && checkFile.canWrite() && imageView != null){
            checkFile = null;
            if (scaleTimes <= 0){
                scaleTimes = 1;
            }
            imageView.setImageBitmap(null);
            imageView.setBackgroundColor(Color.TRANSPARENT);
            String tag = buildTag(path+"Thumbnail");
            imageView.setTag(tag);
            cacheExecutor.submit(new FutureTask<>(new GenerateImageThumbnail(path,tag,imageView,scaleTimes,autoScale,handleOnLoaded)),tag);
        }
    }

好了 , 现在让我们看看最后的成果~~~

这是首次加载的Log:
Android 图片如何高效加载与缓存 (4) —— 生成图片缩略图_第2张图片

这是二次加载的Log:

3GB RAM 手机内存使用状况 (点击查看大图):
Android 图片如何高效加载与缓存 (4) —— 生成图片缩略图_第3张图片

512MB RAM 手机内存使用状况 (点击查看大图):

加载的图像界面:

文章里使用到的线程池以及图像缓存等都是基于之前所介绍过的 , 不清楚的dalao可以去前面的看看哦 =.=

你可能感兴趣的:(android,性能,图片,内存,缓存)