Как избежать OutOfMemoryError даже после использования рекомендаций «Эффективная загрузка большого растрового изображения» - PullRequest
1 голос
/ 29 марта 2019

Я пытаюсь установить setImageBitmap, но продолжаю получать следующее OutOfMemoryError даже после того, как я реализовал рекомендации здесь https://developer.android.com/topic/performance/graphics/load-bitmap.html:

java.lang.OutOfMemoryError: Failed to allocate a 11345668 byte allocation with 1206384 free bytes and 1178KB until OOM
        at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
        at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
        at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:620)
        at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:455)
        at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:478)
        at com.example.myapp.ImageOptimized.decodeSampledBitmapFromResource(ImagenOptimizada.java:50)
        at com.example.myapp.Game3Players.onAnimationEnd(Game3Players.java:511)
        at android.view.animation.Animation$3.run(Animation.java:381)
        at android.os.Handler.handleCallback(Handler.java:751)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6119)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)

Это часть кода, где я использую метод.Я случайным образом выбираю одну карту, чтобы показать одно изображение, а остальные показывают другое:

ImageView[] cardsArray = new ImageView[3];
            cardsArray[0]=cardOne;
            cardsArray[1]=cardTwo;
            cardsArray[2]=cardThree;

            final int index = new Random().nextInt(cardsArray.length);                                
            cardsArray[index].setImageBitmap(ImageOptimized.decodeSampledBitmapFromResource(getResources(), R.drawable.skull, 250, 250));     

            for (int i=0; i<cardsArray.length; i++){                                                  

                if (cardsArray[index]!=cardsArray[i]){
                    cardsArray[i].setImageBitmap(ImageOptimized.decodeSampledBitmapFromResource(getResources(), R.drawable.safe, 250, 250));
                }

            }

Сначала я попробовал это сделать, потому что, поскольку я заменяю изображение на другое, я хочу использовать ту же ширину ивысота:

cardsArray[index].setImageBitmap(ImageOptimized.decodeSampledBitmapFromResource(getResources(), R.drawable.skull, cardsArray[index].getWidth(), cardsArray[index].getHeight()));

Но это сразу дало мне ошибку.Вот почему я решил использовать фиксированный размер, 250, но он продолжает выдавать ошибку.Забавно, что иногда код запускается без проблем, но затем, когда операция повторяется - может быть, в третий, четвертый, иногда пятый раз - происходит сбой.Возможно, я делаю что-то не так, что может привести к утечке памяти?

На тот случай, если вы тоже захотите взглянуть на класс, где я следую инструкциям:

public class ImageOptimized {


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

}

Ответы [ 2 ]

1 голос
/ 29 марта 2019

Вы можете использовать библиотеки загрузки и кэширования изображений для повышения производительности, такие как Glide или Picasso .

Использование Glide, например, довольно просто:

Glide.with(this).load(R.drawable.safe).into(imageView);

Вы также можете изменить их размер:

Glide.with(this).load(R.drawable.safe).apply(new RequestOptions().override(250, 250).into(imageView);

Большинство этих библиотек самостоятельно выполняют очистку используемых ими ресурсов;Это означает, что они эффективно обрабатывают память.

Также старайтесь не использовать изображения с очень большим разрешением и используйте формат PNG, если вы можете выбрать.

0 голосов
/ 29 марта 2019

У меня была такая же проблема несколько дней назад, я пропустил копирование / вставку моего Drawable и вместо того, чтобы поместить его в drawable-hdpi (например), я поместил его в папку drawable без расширения, переместив Drawable в у меня работала рисуемая папка с расширением.

Также вы можете добавить эти строки в ваш manifest.xml

андроид: hardwareAccelerated = "ложь"

андроида: largeHeap = "истинный"

...