处理大型位图

Sam*_*muh 31 performance android bitmap

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

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

我该如何处理这类案件?有没有办法选择性地仅对高分辨率图像进行下采样?

Sam*_*muh 55

BitmapFactory.Options类中有一个选项(我忽略了一个)命名inJustDecodeBounds,其中javadoc读取:

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

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

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

  • 当你找到答案时,+1回答你自己的问题; 想想那些谷歌并找到你的问题的人,但没有答案. (9认同)

Rya*_*ral 13

经过几天努力避免我使用不同设备获得的所有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;
}
Run Code Online (Sandbox Code Playgroud)

这种方法适用于我测试的所有设备,但我认为使用其他我不知道的过程可以更好地提高质量.

我希望我的代码可以在相同的情况下帮助其他开发人员.我也很感激高级开发人员是否可以提供帮助,并提出有关其他流程的建议,以避免在此过程中损失(较少)质量.


Gre*_*reg 12

我自己做的是:

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