设置 Android 查看背景图片有效地给出 java.lang.OutOfMemoryError

Set Android View background image efficiently gives java.lang.OutOfMemoryError

在将背景图像设置为 Android 视图 (API 22) 的过程中,我遇到了一些问题。

我的图像是一个 1MB 的 .jpeg 文件,4000x2672。

起初我尝试通过

android:background="@drawable/your_image"

方法,得到一个D/skia﹕ --- allocation failed for scaled bitmap错误。

然后我就跟着http://developer.android.com/training/displaying-bitmaps/load-bitmap.html实现了如下:

BitmapDecoder.java

(..omissis..)
public class BitmapDecoder {

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

}

SplasActivity.java(主activity)

    @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_splash);
    LinearLayout mL = (LinearLayout)findViewById(R.id.MainLayout);
    Display display = getWindowManager().getDefaultDisplay();
    Point size = new Point();
    display.getSize(size);
    int width = size.x;
    int height = size.y;

    if (Build.VERSION.SDK_INT >= 16) {
        Drawable d = new BitmapDrawable(getResources(),
                BitmapDecoder.decodeSampledBitmapFromResource(getResources(),
                        R.drawable.andromeda,
                        1080,1920));
        mL.setBackground(d);
    }

    this.getSupportActionBar().hide();
}

我正在获取设备屏幕的 WxH,将 Drawable 资源调整为该大小,将生成的位图转换为 Drawable,将 Drawable 设置为视图背景。

但我总是得到

D/skia﹕ --- allocation failed for scaled bitmap
...
java.lang.OutOfMemoryError: Failed to allocate a 684032012 byte allocation with 16777120 free bytes and 227MB until OOM

错误。

我已经搜索了几个小时,但没有成功实施所有建议的技术。

附带说明一下,如果我将宽度和高度设置为非常小的值(例如 400x200),它就可以工作。但是,正如你想象的那样,那是不可行的。

知道如何解决吗?

我认为问题在于您以压缩的 jpeg 格式 (1MB) 评估图像大小,但是当您加载它并在您的软件中进行操作时,它以位图形式解压缩。

正如您提到的 link 中所报道的那样: “...较低分辨率的版本应与显示它的 UI 组件的大小相匹配。具有较高分辨率的图像不会提供任何可见的好处,但仍会占用宝贵的内存并导致额外的性能开销,因为额外的动态缩放。 .."

您使用的是 4000x2672= 10688000 像素的图像,每个像素需要一些字节来编码颜色,因此总共分配了 684032012 个字节。

我能给出的唯一建议是评估您想要在哪个更大的屏幕上显示您的图像,检查它的定义并重新缩放您的图像以便最多匹配该值。

从你的数字来看你把你的jpeg放在res/drawable文件夹里,和res/drawable-mdpi一样,补救办法是把它放在res/drawable-nodpi文件夹里