【问题标题】:Handling large Bitmaps处理大型位图
【发布时间】:2011-01-14 07:47:28
【问题描述】:

我有一堆图片网址。我必须下载这些图像并在我的应用程序中一一显示。我正在使用SoftReferences 将图像保存在集合中,并保存在 SD 卡上,以避免重新获取并改善用户体验。

问题是我对位图的大小一无所知。事实证明,当我使用 BitmapFactory.decodeStream(InputStream) 方法时,我偶尔会收到 OutOfMemoryExceptions。因此,我选择使用 BitmapFactory 选项(样本大小 = 2)对图像进行下采样。这提供了更好的输出:没有 OOM,但这会影响较小图像的质量。

我应该如何处理这种情况?有没有办法选择性地只对高分辨率图像进行下采样?

【问题讨论】:

标签: android performance bitmap


【解决方案1】:

经过几天努力避免使用不同设备时出现的所有 OutOfMemory 错误,我创建了这个:

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;
}

此方法适用于我测试过的所有设备,但我认为使用我不知道的其他流程质量会更好。

我希望我的代码可以帮助其他处于相同情况的开发人员。如果高级开发人员可以提供帮助,我也很感激,提供有关其他流程的建议,以避免在流程中损失(更少)质量。

【讨论】:

  • 好东西。但是 calculateSampleSize 是错误的。我现在已经更新了帖子来修复它。
【解决方案2】:

我自己做的是:

  • 使用inJustDecodeBounds获取图片的原始尺寸
  • 有一个固定的最大 Surface 用于加载位图(比如 1Mpixels)
  • 检查图像表面是否低于限制,如果是,则直接加载它
  • 如果不计算理想的宽度和高度,您应该加载位图以保持低于最大表面(这里有一些简单的数学运算)。这为您提供了在加载位图时要应用的浮动比率
  • 现在您想将此比率转换为合适的inSampleSize(不会降低图像质量的 2 的幂)。我使用这个功能:
int k = Integer.highestOneBit((int)Math.floor(ratio)); 如果(k==0)返回 1; 否则返回 k;
  • 那么由于加载的位图分辨率比最大表面稍高(因为您必须使用较小的 2 次方),您必须调整位图的大小,但速度会快得多。

【讨论】:

  • 感谢您添加例程来计算样本量。它用一条重要信息来扩充线程。
【解决方案3】:

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

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

我用它来找出位图的实际大小,然后选择使用inSampleSize 选项对其进行下采样。这至少可以避免解码文件时出现任何 OOM 错误。

参考:
1.Handling larger Bitmaps
2.How do I get Bitmap info before I decode

【讨论】:

  • +1 用于在您找到答案时回答您自己的问题;想想那些在谷歌上找到你的问题但没有答案的人。
  • 如果我想获取尺寸,它确实可以正常工作,但是当我想将文件解码为位图并将该位图设置为 ImageView 时,我遇到了 OOM 错误。请告诉我如何优化它或任何其他方式来解决这个问题。
猜你喜欢
  • 2012-06-19
  • 1970-01-01
  • 1970-01-01
  • 2012-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-23
相关资源
最近更新 更多