• BitmapFactory.Options详解


    在通过BitmapFactory.decodeFile(String path)方法将突破转成Bitmap时,遇到大一些的图片,我们经常会遇到OOM(Out Of Memory)的问题。怎么避免它呢? 这就用到了我们上面提到的BitmapFactory.Options这个类。

    BitmapFactory.Options这个类,有一个字段叫做 inJustDecodeBounds 。SDK中对这个成员的说明是这样的: If set to true, the decoder will return null (no bitmap), but the out… 也就是说,如果我们把它设为true,那么BitmapFactory.decodeFile(String path, Options opt)并不会真的返回一个Bitmap给你,它仅仅会把它的宽,高取回来给你,这样就不会占用太多的内存,也就不会那么频繁的发生OOM了。

     

    示例代码如下:

    1. BitmapFactory.Options options = new BitmapFactory.Options();
    2. options.inJustDecodeBounds = true;
    3. Bitmap bmp = BitmapFactory.decodeFile(path, options);

    复制代码

    这段代码之后,options.outWidth 和 options.outHeight就是我们想要的宽和高了。

    有了宽,高的信息,我们怎样在图片不变形的情况下获取到图片指定大小的缩略图呢? 比如我们需要在图片不变形的前提下得到宽度为200的缩略图。 那么我们需要先计算一下缩放之后,图片的高度是多少 

    1. int height = options.outHeight * 200 / options.outWidth;
    2. options.outWidth = 200;
    3. options.outHeight = height; 
    4. options.inJustDecodeBounds = false;
    5. Bitmap bmp = BitmapFactory.decodeFile(path, options);
    6. image.setImageBitmap(bmp);

    复制代码

    这样虽然我们可以得到我们期望大小的ImageView 但是在执行BitmapFactory.decodeFile(path, options);时,并没有节约内存。要想节约内存,还需要用到BitmapFactory.Options这个类里的 inSampleSize 这个成员变量。 我们可以根据图片实际的宽高和我们期望的宽高来计算得到这个值。

    1. inSampleSize = options.outWidth / 200;

    另外,为了节约内存我们还可以使用下面的几个字段:

    1. options.inPreferredConfig = Bitmap.Config.ARGB_4444;    // 默认是Bitmap.Config.ARGB_8888
    2. options.inPurgeable = true;
    3. options.inInputShareable = true;

    BitmapFactory.Options.inSampleSize

    设置恰当的inSampleSize可以使BitmapFactory分配更少的空间以消除该错误。inSampleSize的具体含义请参考SDK文档。例如:

    BitmapFactory.Options opts = new BitmapFactory.Options();
    opts.inSampleSize = 4;
    Bitmap bitmap = BitmapFactory.decodeFile(imageFile, opts);

    设置恰当的inSampleSize是解决该问题的关键之一。BitmapFactory.Options提供了另一个成员inJustDecodeBounds。

    BitmapFactory.Options opts = new BitmapFactory.Options();
    
    opts.inJustDecodeBounds = true;
    
    Bitmap bitmap = BitmapFactory.decodeFile(imageFile, opts);

    设置inJustDecodeBounds为true后,decodeFile并不分配空间,但可计算出原始图片的长度和宽度,即opts.width和opts.height。有了这两个参数,再通过一定的算法,即可得到一个恰当的inSampleSize。

    查看Android源码,我们得知,为了得到恰当的inSampleSize,Android提供了一种动态计算的方法。

    public static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
        int initialSize = computeInitialSampleSize(options, minSideLength, maxNumOfPixels);
        int roundedSize;
        if (initialSize <= 8) {
            roundedSize = 1;
            while (roundedSize < initialSize) {
                roundedSize <<= 1;
            }
        } else {
            roundedSize = (initialSize + 7) / 8 * 8;
        }
        return roundedSize;
    }
    
    private static int computeInitialSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
        double w = options.outWidth;
        double h = options.outHeight;
        int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
        int upperBound = (minSideLength == -1) ? 128 : (int) Math.min(Math.floor(w / minSideLength), Math.floor(h / minSideLength));
        if (upperBound < lowerBound) {
            // return the larger one when there is no overlapping zone.
            return lowerBound;
        }
        if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
            return 1;
        } else if (minSideLength == -1) {
            return lowerBound;
        } else {
            return upperBound;
        }
    } 
    
    使用该算法,就可动态计算出图片的inSampleSize。
    
    BitmapFactory.Options opts = new BitmapFactory.Options();
    opts.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(imageFile, opts);
    opts.inSampleSize = computeSampleSize(opts, -1, 128*128);  
    opts.inJustDecodeBounds = false;
    try {
       bmp = BitmapFactory.decodeFile(imageFile, opts);
       imageView.setImageBitmap(bmp);
    } catch (OutOfMemoryError err) {
    }

    源代码如下:

        public static Bitmap createImageThumbnail(String filePath){
             Bitmap bitmap = null;
             BitmapFactory.Options opts = new BitmapFactory.Options();
             opts.inJustDecodeBounds = true;
             BitmapFactory.decodeFile(filePath, opts);
    
             opts.inSampleSize = computeSampleSize(opts, -1, 128*128);
             opts.inJustDecodeBounds = false;
    
             try {
                 bitmap = BitmapFactory.decodeFile(filePath, opts);
             }catch (Exception e) {
                // TODO: handle exception
            }
            return bitmap;
        }
    
        public static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
            int initialSize = computeInitialSampleSize(options, minSideLength, maxNumOfPixels);
            int roundedSize;
            if (initialSize <= 8) {
                roundedSize = 1;
                while (roundedSize < initialSize) {
                    roundedSize <<= 1;
                }
            } else {
                roundedSize = (initialSize + 7) / 8 * 8;
            }
            return roundedSize;
        }
    
        private static int computeInitialSampleSize(BitmapFactory.Options options,int minSideLength, int maxNumOfPixels) {
            double w = options.outWidth;
            double h = options.outHeight;
            int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
            int upperBound = (minSideLength == -1) ? 128 :(int) Math.min(Math.floor(w / minSideLength), Math.floor(h / minSideLength));
            if (upperBound < lowerBound) {
                // return the larger one when there is no overlapping zone.
                return lowerBound;
            }
            if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
                return 1;
            } else if (minSideLength == -1) {
                return lowerBound;
            } else {
                return upperBound;
            }
        }

    项目实战代码:

    /**
         * 不full load图片,先根据一定的算法缩小再load
         * 
         * @param path
         * @return
         */
        public static Bitmap loadBitmapFromFile(String path, int thumbWidth, int thumbHeight) {
            Bitmap bmp = null;
            
            BitmapFactory.Options opts = new BitmapFactory.Options();
            /*
             * 设置inJustDecodeBounds为true后,decodeFile并不分配空间,但可计算出原始图片的长度和宽度,即opts.width和opts.height。
             * 有了这两个参数,再通过一定的算法,即可得到一个恰当的inSampleSize
             */
            opts.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(path, opts);
            
            opts.inSampleSize = calculateInSampleSize(opts, thumbWidth, thumbHeight);
            //这里一定要将其设置回false,因为之前我们将其设置成了true     
            opts.inJustDecodeBounds = false;
            
            try {
                bmp = BitmapFactory.decodeFile(path, opts);
            } catch (OutOfMemoryError e) {
                Logger.e(TAG, e.getMessage());
            }
            
            return bmp;
        }
        
        //计算图片的缩放值
        public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
            final int height = options.outHeight;
            final int width = options.outWidth;
            int inSampleSize = 1;
    
            if (height > reqHeight || width > reqWidth) {
                final int heightRatio = Math.round((float) height / (float) reqHeight);
                final int widthRatio = Math.round((float) width / (float) reqWidth);
                inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
            }
    
            return inSampleSize;
        }
        
        
        /**
         * 不full load图片,先根据一定的算法缩小再load
         * 
         * @param path
         * @return
         */
        public static Bitmap loadBitmapFromFile(String path) {
            Bitmap bmp = null;
            
            BitmapFactory.Options opts = new BitmapFactory.Options();
            /*
             * 设置inJustDecodeBounds为true后,decodeFile并不分配空间,但可计算出原始图片的长度和宽度,即opts.width和opts.height。
             * 有了这两个参数,再通过一定的算法,即可得到一个恰当的inSampleSize
             */
            opts.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(path, opts);
            
            opts.inSampleSize = computeSampleSize(opts, -1, 512 * 512);
            //这里一定要将其设置回false,因为之前我们将其设置成了true     
            opts.inJustDecodeBounds = false;
            
            try {
                bmp = BitmapFactory.decodeFile(path, opts);
            } catch (OutOfMemoryError e) {
                Logger.e(TAG, e.getMessage());
            }
            
            return bmp;
        }
        
        public static Bitmap loadBitmapFromUrl(Context ctx, String url) {
            File path =  ctx.getCacheDir();
            String hashedURLString = hashURLString(url);
            
            String finalPath = new File(path, hashedURLString).getAbsolutePath();
            
            boolean ok = FileUtils.saveUrl(finalPath, url);
            if (ok) {
                return loadBitmapFromFile(finalPath);
            }
            
            return null;
        }    
        /**
         * 将图片先放到临时空间中,再读取图片。full load
         * 
         * @param path
         * @return
         */
        public static Bitmap loadBitmapFromFileX(String path) {
            BitmapFactory.Options bfOptions = new BitmapFactory.Options();
            bfOptions.inDither = false;
            bfOptions.inPurgeable = true;
            bfOptions.inTempStorage = new byte[12 * 1024];
            File file = new File(path);
            FileInputStream fs = null;
    
            Bitmap bmp = null;
    
            try {
                fs = new FileInputStream(file);
                bmp = BitmapFactory.decodeFileDescriptor(fs.getFD(), null, bfOptions);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (OutOfMemoryError e) {
                e.printStackTrace();
            } finally {
                try {
                    if (fs != null) {
                        fs.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
            return bmp;
        }
        
        public static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
            int initialSize = computeInitialSampleSize(options, minSideLength, maxNumOfPixels);
    
            int roundedSize;
            if (initialSize <= 8) {
                roundedSize = 1;
                while (roundedSize < initialSize) {
                    roundedSize <<= 1;
                }
            } else {
                roundedSize = (initialSize + 7) / 8 * 8;
            }
    
            return roundedSize;
        }
    
        private static int computeInitialSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
            double w = options.outWidth;
            double h = options.outHeight;
    
            int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
            int upperBound = (minSideLength == -1) ? 128 : (int) Math.min(Math.floor(w / minSideLength), Math.floor(h / minSideLength));
    
            if (upperBound < lowerBound) {
                return lowerBound;
            }
    
            if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
                return 1;
            } else if (minSideLength == -1) {
                return lowerBound;
            } else {
                return upperBound;
            }
        }
        
        private static String hashURLString(String urlString) {
            try {
                // Create MD5 Hash
                MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
                digest.update(urlString.getBytes());
                byte messageDigest[] = digest.digest();
                
                // Create Hex String
                StringBuffer hexString = new StringBuffer();
                for (int i=0; i<messageDigest.length; i++)
                    hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
                return hexString.toString();
                
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            }
            
            //fall back to old method
            return urlString.replaceAll("[^A-Za-z0-9]", "#");
        }
  • 相关阅读:
    作业十三
    作业十二
    第十一次作业
    编译原理第十次作业
    P3388 【模板】割点(割顶) 题解 (Tarjan)
    BuaacodingT141 microhhh的回城 题解(模拟)
    P2055 [ZJOI2009]假期的宿舍 题解(二分图)
    P2764 最小路径覆盖问题 题解(二分图)
    2019.2-2019.3 TO-DO LIST
    P3369 【模板】普通平衡树 题解(Splay/FHQ)
  • 原文地址:https://www.cnblogs.com/Yang2/p/3584948.html
Copyright © 2020-2023  润新知