Throwing OutOfMemoryError "Не удалось выделить распределение байтов со свободными байтами и до OOM" - PullRequest
0 голосов
/ 26 апреля 2020

Я пытаюсь сделать игровое приложение. У меня есть 3 вида деятельности (интро, игра / основной и геймплей). Игра работает нормально с первой по третью попытки, но в четвертый раз, пытаясь повторить попытку (есть кнопка, чтобы повторить попытку при выполнении игры, которая возвращает вас к основному действию), я получаю эту ошибку: E / art: throwing OutOfMemoryError "Не удалось выделить 27581052 байта для выделения с 7416608 свободных байтов и до OOM"

Я уже пытался: 1) изменить размер изображений / растровых изображений, с которыми я работаю; 2) поместите изображения в разные папки для рисования, такие как xhdpi, xxxhdpi (и все между ними ...); 3) добавление android:hardwareAccelerated="false" и android:largeHeap="true" к манифесту; И все они только что купили мне еще несколько попыток повторной попытки, но не решили проблему (приложение получает такое же сообщение об ошибке после 8 ~ 10 попыток).

Я также пробовал их, так как я нашел их на некоторых Ответы здесь, на этом форуме: 4) реализация ComponentCallbacks2 onTrimMemory (https://developer.android.com/topic/performance/memory.html) во время игры или основных действий; 5) добавление чего-то подобного в игровой процесс или в основные виды деятельности:

@Override
public void onDestroy() {
    super.onDestroy();
    Runtime.getRuntime().gc();      
}

И это не изменило мою проблему ...

Вот Logcat:

04-26 00:22:39.665 31000-31000/com.example.theflyingdactylgameapp I/Timeline: Timeline: Activity_launch_request time:22242888
04-26 00:22:39.755 31000-31000/com.example.theflyingdactylgameapp I/art: Alloc sticky concurrent mark sweep GC freed 2897(97KB) AllocSpace objects, 0(0B) LOS objects, 5% free, 121MB/128MB, paused 548us total 6.523ms
04-26 00:22:39.775 31000-31000/com.example.theflyingdactylgameapp I/art: Clamp target GC heap from 137MB to 128MB
04-26 00:22:39.775 31000-31000/com.example.theflyingdactylgameapp I/art: Alloc partial concurrent mark sweep GC freed 1719(109KB) AllocSpace objects, 0(0B) LOS objects, 5% free, 121MB/128MB, paused 394us total 13.719ms
04-26 00:22:39.795 31000-31000/com.example.theflyingdactylgameapp I/art: Clamp target GC heap from 136MB to 128MB
04-26 00:22:39.795 31000-31000/com.example.theflyingdactylgameapp I/art: Alloc concurrent mark sweep GC freed 1089(84KB) AllocSpace objects, 0(0B) LOS objects, 5% free, 120MB/128MB, paused 473us total 21.972ms
04-26 00:22:39.795 31000-31000/com.example.theflyingdactylgameapp I/art: Forcing collection of SoftReferences for 26MB allocation
04-26 00:22:39.815 31000-31000/com.example.theflyingdactylgameapp I/art: Clamp target GC heap from 136MB to 128MB
04-26 00:22:39.815 31000-31000/com.example.theflyingdactylgameapp I/art: Alloc concurrent mark sweep GC freed 12(384B) AllocSpace objects, 0(0B) LOS objects, 5% free, 120MB/128MB, paused 386us total 17.719ms
04-26 00:22:39.815 31000-31000/com.example.theflyingdactylgameapp E/art: Throwing OutOfMemoryError "Failed to allocate a 27581052 byte allocation with 7416672 free bytes and 7MB until OOM"
04-26 00:22:39.825 31000-31000/com.example.theflyingdactylgameapp I/art: Clamp target GC heap from 136MB to 128MB
04-26 00:22:39.825 31000-31000/com.example.theflyingdactylgameapp I/art: Alloc partial concurrent mark sweep GC freed 6(192B) AllocSpace objects, 0(0B) LOS objects, 5% free, 120MB/128MB, paused 387us total 7.547ms
04-26 00:22:39.845 31000-31000/com.example.theflyingdactylgameapp I/art: Clamp target GC heap from 136MB to 128MB
04-26 00:22:39.845 31000-31000/com.example.theflyingdactylgameapp I/art: Alloc concurrent mark sweep GC freed 3(96B) AllocSpace objects, 0(0B) LOS objects, 5% free, 120MB/128MB, paused 368us total 17.285ms
04-26 00:22:39.845 31000-31000/com.example.theflyingdactylgameapp I/art: Forcing collection of SoftReferences for 26MB allocation
04-26 00:22:39.855 31000-31000/com.example.theflyingdactylgameapp I/art: Clamp target GC heap from 136MB to 128MB
04-26 00:22:39.855 31000-31000/com.example.theflyingdactylgameapp I/art: Alloc concurrent mark sweep GC freed 3(96B) AllocSpace objects, 0(0B) LOS objects, 5% free, 120MB/128MB, paused 551us total 17.798ms
04-26 00:22:39.855 31000-31000/com.example.theflyingdactylgameapp E/art: Throwing OutOfMemoryError "Failed to allocate a 27581052 byte allocation with 7416608 free bytes and 7MB until OOM"
04-26 00:22:39.855 31000-31000/com.example.theflyingdactylgameapp D/skia: --- allocation failed for scaled bitmap
04-26 00:22:39.855 31000-31000/com.example.theflyingdactylgameapp D/AndroidRuntime: Shutting down VM
04-26 00:22:39.865 31000-31000/com.example.theflyingdactylgameapp E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.theflyingdactylgameapp, PID: 31000
    java.lang.OutOfMemoryError: Failed to allocate a 27581052 byte allocation with 7416608 free bytes and 7MB until OOM
        at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
        at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
        at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:613)
        at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:446)
        at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:469)
        at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:501)
        at com.example.theflyingdactylgameapp.FlyingDactylView.<init>(FlyingDactylView.java:57)
        at com.example.theflyingdactylgameapp.MainActivity.onCreate(MainActivity.java:21)
        at android.app.Activity.performCreate(Activity.java:6041)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1109)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2283)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2392)
        at android.app.ActivityThread.access$800(ActivityThread.java:154)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1308)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:135)
        at android.app.ActivityThread.main(ActivityThread.java:5273)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:908)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:703)
04-26 00:22:39.875 31000-31000/com.example.theflyingdactylgameapp I/Process: Sending signal. PID: 31000 SIG: 9

А вот игровая активность:

package com.example.theflyingdactylgameapp;

import ...

public class GameOverActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_game_over);

        int score = (int) getIntent().getExtras().get("POPOINTS");
        Button startGameAgain = findViewById(R.id.play_again_btn);
        TextView displayScore = findViewById(R.id.displayScore);
        TextView displayRecord = findViewById(R.id.displayRecord);


        //update record
        SharedPreferences settings =getSharedPreferences("RECORD", Context.MODE_PRIVATE);
        int record = settings.getInt("RECORD", 0);

        String score1;
        String record1;
        if (score > record) {
            record = score;
            record1 = Integer.toString(record);
            score1 = Integer.toString(score);
            displayScore.setText(String.format("POPOINTS = %s", score1));
            displayRecord.setText(String.format("*NEW RECORD* = %s", record1));


            SharedPreferences.Editor editor = settings.edit();
            editor.putInt("RECORD", record);
            editor.apply();
        }
        else {
            record1 = Integer.toString(record);
            score1 = Integer.toString(score);
            displayScore.setText(String.format("POPOINTS = %s", score1));
            displayRecord.setText(String.format("record = %s", record1));
        }


        startGameAgain.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent mainIntent = new Intent (GameOverActivity.this, MainActivity.class);
                startActivity(mainIntent);
            }
        });



    }


}

А вот основная деятельность:

package com.example.theflyingdactylgameapp;

import...

public class MainActivity extends AppCompatActivity {

    private FlyingDactylView gameView;
    private Handler handler = new Handler();
    private final static long Interval = 30;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        gameView = new FlyingDactylView(this);
        setContentView(gameView);

        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                      gameView.invalidate();
                    }
                });
            }
        }, 0, Interval);
    }


}

, а вот представление, используемое основной деятельностью:

package com.example.theflyingdactylgameapp;

import ...
public class FlyingDactylView extends View {

    private Bitmap dactyl[] = new Bitmap[2];
    private int dactylX = 10;
    private int dactylY;
    private int dactylSpeed;

    private int canvasWidth, canvasHeight;

    private int popo0X, popo0Y, popo0Speed = 16;
    private int popo2X, popo2Y, popo2Speed = 18;
    private int popo1X, popo1Y, popo1Speed = 20;
    private Bitmap popo[] = new Bitmap[2];

    private int redX, redY, redSpeed = 24;
    private int red1X, red1Y, red1Speed = 27;
    private Bitmap redBall;

    private int score, lifeCounterOfDactyl;

    private boolean touch = false;

    private Bitmap backgroundImage;
    private Paint scorePaint = new Paint();
    private Bitmap life[] = new Bitmap[2];

    // creating sound
    private SoundPool soundPool;
    private int sound1, sound2, sound3;


    // criando objetos na tela
    public FlyingDactylView(Context context) {
        super(context);

        dactyl[0] = BitmapFactory.decodeResource(getResources(), R.drawable.dactyl1);
        dactyl[1] = BitmapFactory.decodeResource(getResources(), R.drawable.dactyl2);

        backgroundImage = BitmapFactory.decodeResource(getResources(), R.drawable.background);

        popo[0] = BitmapFactory.decodeResource(getResources(), R.drawable.popo1);

        popo[1] = BitmapFactory.decodeResource(getResources(), R.drawable.popo2);

        redBall = BitmapFactory.decodeResource(getResources(), R.drawable.red_ball);

        scorePaint.setColor(Color.WHITE);
        scorePaint.setTextSize(70);
        scorePaint.setTypeface(Typeface.DEFAULT_BOLD);
        scorePaint.setAntiAlias(true);

        life[0] = BitmapFactory.decodeResource(getResources(), R.drawable.egg);
        life[1] = BitmapFactory.decodeResource(getResources(), R.drawable.broken_egg);

        dactylY = 550;
        score = 0;
        lifeCounterOfDactyl = 3;

        // creating sound player and placing sounds
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            AudioAttributes audioAttributes = new AudioAttributes.Builder()
                    .setUsage(AudioAttributes.USAGE_GAME)
                    .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                    .build();
            soundPool = new SoundPool.Builder()
                    .setMaxStreams(5)
                    .setAudioAttributes(audioAttributes)
                    .build();
        }
        else {
            soundPool = new SoundPool(5, AudioManager.STREAM_MUSIC, 0);
        }
        sound1 = soundPool.load(this.getContext(), R.raw.fly, 1);
        sound2 = soundPool.load(this.getContext(), R.raw.pick_popo, 1);
        sound3 = soundPool.load(this.getContext(), R.raw.red_hit, 1);




    }
    // movimentos
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvasWidth = canvas.getWidth();
        canvasHeight = canvas.getHeight();

        canvas.drawBitmap(backgroundImage,0, 0, null);

        // movimentos dactyl
        int minDactylY = dactyl[0].getHeight();
        int maxDactylY = canvasHeight - dactyl[0].getHeight() * 3;
        dactylY += dactylSpeed;
        if (dactylY < minDactylY) {
            dactylY = minDactylY;
        }
        if (dactylY > maxDactylY) {
            dactylY = maxDactylY;
        }
        dactylSpeed += 2;

        if (touch) {
            canvas.drawBitmap(dactyl[1], dactylX, dactylY, null);
            touch = false;
        }
        else {
            canvas.drawBitmap(dactyl[0], dactylX, dactylY, null);
        }


        // movimentos popó
        popo0X -= popo0Speed;

        if (hitPopoChecker(popo0X, popo0Y)) {
            score += 10;
            popo0X = -100;
            // sound when hit popo0
            soundPool.play(sound2, 1, 1,0, 0, 1);
        }

        if (popo0X < 0) {
            popo0X = canvasWidth + 21;
            popo0Y = (int) Math.floor(Math.random() * (maxDactylY - minDactylY)) + minDactylY;
        }

        canvas.drawBitmap(popo[0], popo0X, popo0Y, null);



        // movimentos popó2
        popo2X -= popo2Speed;

        if (hitPopoChecker(popo2X, popo2Y)) {
            score += 10;
            popo2X = -100;
            // sound when hit popo2
            soundPool.play(sound2, 1, 1,0, 0, 1);
        }

        if (popo2X < 0) {
            popo2X = canvasWidth + 21;
            popo2Y = (int) Math.floor(Math.random() * (maxDactylY - minDactylY)) + minDactylY;
        }

        canvas.drawBitmap(popo[0], popo2X, popo2Y, null);


        // movimentos popó gordo
        popo1X -= popo1Speed;

        if (hitPopoChecker(popo1X, popo1Y)) {
            score += 20;
            popo1X = -100;
            // sound when hit popo gordo
            soundPool.play(sound2, 1, 1,0, 0, 1);
        }

        if (popo1X < 0) {
            popo1X = canvasWidth + 21;
            popo1Y = (int) Math.floor(Math.random() * (maxDactylY - minDactylY)) + minDactylY;
        }

        canvas.drawBitmap(popo[1], popo1X, popo1Y, null);


        // movimentos Red Ball
        redX -= redSpeed;

        if (hitPopoChecker(redX, redY)) {
            redX = -100;
            lifeCounterOfDactyl--;
            // sound when hit red ball 1
            soundPool.play(sound3, 1, 1,0, 0, 1);

            if (lifeCounterOfDactyl == 0) {
                Toast.makeText(getContext(), "Game Over", Toast.LENGTH_SHORT).show();

                Intent gameOverIntent = new Intent(getContext(), GameOverActivity.class);
                // free up resources from sound on Game Over by red ball 1
                soundPool.release();
                soundPool = null;

                gameOverIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                gameOverIntent.putExtra("POPOINTS", score);
                getContext().startActivity(gameOverIntent);
            }
        }

        if (redX < 0) {
            redX = canvasWidth + 21;
            redY = (int) Math.floor(Math.random() * (maxDactylY - minDactylY)) + minDactylY;
        }

        canvas.drawBitmap(redBall, redX, redY, null);

        // movimentos Red Ball2
        red1X -= red1Speed;

        if (hitPopoChecker(red1X, red1Y)) {
            red1X = -100;
            lifeCounterOfDactyl--;
            // sound when hit red ball 1
            soundPool.play(sound3, 1, 1,0, 0, 1);

            if (lifeCounterOfDactyl == 0) {
                Toast.makeText(getContext(), "Game Over", Toast.LENGTH_SHORT).show();

                Intent gameOverIntent = new Intent(getContext(), GameOverActivity.class);
                // free up resources from sound on Game Over by red ball 2
                soundPool.release();
                soundPool = null;

                gameOverIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                gameOverIntent.putExtra("POPOINTS", score);
                getContext().startActivity(gameOverIntent);
            }
        }

        if (red1X < 0) {
            red1X = canvasWidth + 21;
            red1Y = (int) Math.floor(Math.random() * (maxDactylY - minDactylY)) + minDactylY;
        }

        canvas.drawBitmap(redBall, red1X, red1Y, null);




        // Score
        canvas.drawText("Popoints: " + score, 20, 60, scorePaint);

        // life counter
        for (int i=0; i<3; i++) {
            int x = (int) (700 + life[0].getWidth() * 1.9 * i);
            int y = 10;

            if (i < lifeCounterOfDactyl) {
                canvas.drawBitmap(life[0], x, y, null);
            }
            else {
                canvas.drawBitmap(life[1], x, y, null);
            }
        }



    }

    public Boolean hitPopoChecker (int x, int y) {
        if (dactylX < x && x < (dactylX + dactyl[0].getWidth()) && dactylY < y && y < (dactylY + dactyl[0].getHeight())) {
            return true;
        }
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            touch = true;
            // decrease here to make Dactyl goes faster
            dactylSpeed = -22;
            // sound when tap to fly
            soundPool.play(sound1, 1, 1,0, 0, 1);
        }
        return true;
    }

}

Любой совет о том, как попытаться решить его, будет высоко оценен, спасибо!

1 Ответ

1 голос
/ 26 апреля 2020

Когда вы создаете задание таймера в MainActivity.onCreate, оно сохраняет ссылку на действие. Эта ссылка никогда не может быть очищена, потому что поток таймера никогда не останавливается, поэтому память, использованную для действия, никогда не может быть возвращена и использована повторно. Это называется утечкой памяти.

Необходимо сохранить ссылку на таймер в действии и отменить таймер в onDestroy.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...