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]", "#");
    }

 

 

转载于:https://www.cnblogs.com/Yang2/p/3584948.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`BitmapFactory.Options` 是一个用于配置位图加载选项的类。使用 `BitmapFactory.Options` 可以控制位图的加载方式,如缩放、解码格式、采样率等。以下是一个简单的使用示例: ```java // 加载图片的选项 BitmapFactory.Options options = new BitmapFactory.Options(); // 设置为 true,只获取图片的宽度和高度,不加载图片到内存中 options.inJustDecodeBounds = true; BitmapFactory.decodeResource(getResources(), R.drawable.my_image, options); // 计算采样率 int sampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // 设置采样率 options.inSampleSize = sampleSize; // 关闭只获取图片宽度和高度的选项 options.inJustDecodeBounds = false; // 加载图片到内存中 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.my_image, options); ``` 以上示例中,首先使用 `inJustDecodeBounds` 选项将 `BitmapFactory.decodeResource()` 方法的返回值设置为 null,并且只获取图片的宽度和高度,而不是真正地将图片加载到内存中。接着,根据目标宽度和高度计算采样率,然后将采样率设置到 `inSampleSize` 选项中。最后,关闭 `inJustDecodeBounds` 选项并重新调用 `BitmapFactory.decodeResource()` 方法来加载图片到内存中。 注意,`calculateInSampleSize()` 方法需要自己实现。它的作用是根据目标宽度和高度计算出最接近的采样率。以下是一个示例实现: ```java private 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 halfHeight = height / 2; final int halfWidth = width / 2; while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { inSampleSize *= 2; } } return inSampleSize; } ``` 以上示例中,`calculateInSampleSize()` 方法会根据图片的宽度和高度以及目标宽度和高度计算出最接近的采样率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值