在 recyclerview 中使用位图时出现内存不足错误

out of memory error when using bitmap in recyclerview

我想在可绘制目录中的图像视图中显示图像。我正在解码位图并设置为图像视图,但不幸的是它给我内存不足的错误。

请帮帮我...

嘿,您不必将可绘制对象解码为位图。

你可以这样做。

imageview.setImageResource(R.drawable.whatever_the_image_is)

希望对您有所帮助

Android Training class, "Displaying Bitmaps Efficiently",为理解和处理加载位图时的异常 java.lang.OutOfMemoryError: bitmap size exceeds VM budget 提供了一些重要信息。


读取位图尺寸和类型

BitmapFactory class 提供了几种解码方法(decodeByteArray()decodeFile()decodeResource() 等)用于创建 Bitmap来自各种来源。根据您的图像数据源选择最合适的解码方法。这些方法试图为构造的位图分配内存,因此很容易导致 OutOfMemory 异常。每种类型的解码方法都有额外的签名,让您可以通过 BitmapFactory.Options class 指定解码选项。在解码时将 inJustDecodeBounds 属性 设置为 true 可避免内存分配,为位图对象返回 null 但设置 outWidthoutHeightoutMimeType。此技术允许您在构建(和内存分配)位图之前读取图像数据的尺寸和类型。

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

为避免 java.lang.OutOfMemory 异常,请在解码之前检查位图的尺寸,除非您绝对相信来源会为您提供大小可预测且适合可用内存的图像数据。


将缩小版载入内存

现在图像尺寸已知,它们可用于决定是否应将完整图像加载到内存中,或者是否应加载子采样版本。以下是一些需要考虑的因素:

  • 在内存中加载完整图像的估计内存使用量。
  • 考虑到您的应用程序的任何其他内存要求,您愿意承诺加载此图像的内存量。
  • 图像要加载到的目标 ImageView 或 UI 组件的尺寸。
  • 当前设备的屏幕尺寸和密度。

例如,如果一张 1024x768 像素的图像最终会以 128x96 像素的缩略图显示在 ImageView 中,那么将它加载到内存中是不值得的。

要告诉解码器对图像进行子采样,将较小的版本加载到内存中,请在 BitmapFactory.Options 对象中将 inSampleSize 设置为 true。例如,分辨率为 2048x1536 的图像使用 inSampleSize 为 4 进行解码会生成大约 512x384 的位图。将其加载到内存中使用 0.75MB 而不是完整图像的 12MB(假设位图配置为 ARGB_8888)。下面是一种根据目标宽度和高度计算样本大小值的方法:

public static int calculateInSampleSize(
        BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    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;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

Note: A power of two value is calculated because the decoder uses a final value by rounding down to the nearest power of two, as per the inSampleSize documentation.

要使用此方法,首先将 inJustDecodeBounds 设置为 true 进行解码,传递选项,然后使用新的 inSampleSize 值和 inJustDecodeBounds 设置再次解码到 false:

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
    int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

此方法可以轻松地将任意大尺寸的位图加载到显示 100x100 像素缩略图的 ImageView 中,如以下示例代码所示:

mImageView.setImageBitmap(
    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

您可以按照类似的过程解码来自其他来源的位图,方法是根据需要替换适当的 BitmapFactory.decode* 方法。

来源:

尝试将此添加到您的清单文件中。

android:largeHeap="true"

像这样

<application
        android:name=".MainApp"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:largeHeap="true"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

希望对您有所帮助。