处理大的位图

我有一堆图片url。 我必须下载这些图像并逐个显示在我的应用程序中。 我使用SoftReferences和Sdcard将图像保存在一个集合中,以避免重读和改善用户体验。

问题是我不知道有关位图的大小。 事实certificate,当我使用BitmapFactory.decodeStream(InputStream)方法时,我偶尔会发生OutOfMemoryException BitmapFactory.decodeStream(InputStream) 。 所以,我select使用BitmapFactory选项(sample size = 2)对图像进行缩减采样。 这给了更好的输出:没有OOM,但是这会影响较小图像的质量。

我应该如何处理这种情况? 有没有办法有select地降低采样高分辨率的图像?

BitmapFactory.Options类中有一个选项(我忽略了一个),名为inJustDecodeBounds ,其中的javadoc的内容如下:

如果设置为true,解码器将返回null(无位图),但out …字段仍将被设置,允许调用者查询位图而不必为其像素分配内存。

我用它来查找位图的实际大小,然后select使用inSampleSize选项向下采样。 这至less可以在解码文件时避免任何OOM错误。

参考:
1. 处理较大的位图
2. 解码之前如何获取位图信息?

我自己做的是:

  • 使用inJustDecodeBounds来获取图像的原始大小
  • 有一个固定的最大曲面用于加载位图(比如1M像素)
  • 检查图像表面是否低于极限,如果是,则直接加载
  • 如果不计算理想的宽度和高度,在这个宽度和高度上你应该加载位图,使其保持在最大表面之下(这里有一些简单的math运算)。 这给你一个你想在加载位图时应用的浮点率
  • 现在你想把这个比例转换成合适的inSampleSize (2的幂次不会降低图像质量)。 我使用这个function:
 int k = Integer.highestOneBit((int)Math.floor(ratio));
如果(k == 0)返回1;
否则返回k;
  • 那么因为Bitmap已经被加载了比最大表面稍高的分辨率(因为你必须使用较小的2的幂),所以你将不得不调整位图的大小,但是它会快得多。

经过几天的努力,以避免我用不同的设备获得的所有OutOfMemory错误,我创build这个:

 private Bitmap getDownsampledBitmap(Context ctx, Uri uri, int targetWidth, int targetHeight) { Bitmap bitmap = null; try { BitmapFactory.Options outDimens = getBitmapDimensions(uri); int sampleSize = calculateSampleSize(outDimens.outWidth, outDimens.outHeight, targetWidth, targetHeight); bitmap = downsampleBitmap(uri, sampleSize); } catch (Exception e) { //handle the exception(s) } return bitmap; } private BitmapFactory.Options getBitmapDimensions(Uri uri) throws FileNotFoundException, IOException { BitmapFactory.Options outDimens = new BitmapFactory.Options(); outDimens.inJustDecodeBounds = true; // the decoder will return null (no bitmap) InputStream is= getContentResolver().openInputStream(uri); // if Options requested only the size will be returned BitmapFactory.decodeStream(is, null, outDimens); is.close(); return outDimens; } private int calculateSampleSize(int width, int height, int targetWidth, int targetHeight) { int inSampleSize = 1; if (height > targetHeight || width > targetWidth) { // Calculate ratios of height and width to requested height and // width final int heightRatio = Math.round((float) height / (float) targetHeight); final int widthRatio = Math.round((float) width / (float) targetWidth); // Choose the smallest ratio as inSampleSize value, this will // guarantee // a final image with both dimensions larger than or equal to the // requested height and width. inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } return inSampleSize; } private Bitmap downsampleBitmap(Uri uri, int sampleSize) throws FileNotFoundException, IOException { Bitmap resizedBitmap; BitmapFactory.Options outBitmap = new BitmapFactory.Options(); outBitmap.inJustDecodeBounds = false; // the decoder will return a bitmap outBitmap.inSampleSize = sampleSize; InputStream is = getContentResolver().openInputStream(uri); resizedBitmap = BitmapFactory.decodeStream(is, null, outBitmap); is.close(); return resizedBitmap; } 

这种方法适用于我testing过的所有设备,但是我认为使用其他不知道的stream程可以提高质量。

我希望我的代码可以帮助其他开发人员在相同的情况下。 我也很感激一位高级开发人员能否提供帮助,并提出一个关于其他程序的build议,以避免在这个过程中丢失(less)质量。