Прорисовка анимации, вызывающая OutOfMemoryError при втором запуске в Android - PullRequest
8 голосов
/ 31 января 2012

Я сейчас занимаюсь разработкой игры и пытаюсь использовать один AnimationDrawable, чтобы показать одну анимацию в моем главном меню. Когда я запускаю игру один раз, она работает отлично. Но если я нажму кнопку «Назад», когда я снова открою игру, она выдаст OutOfMemorryError. Если я попадаю домой и возвращаюсь к игре, она загружается, но не показывает анимацию, она пуста, где должна быть.

Я думаю, что когда он снова открывает игру, он пытается загрузить анимацию, но он уже загружен с предыдущего запуска, могу ли я как-нибудь освободить эту память? Как я могу обработать это исключение?

Осматривая, я обнаружил, что это общая проблема в Android, но я не нашел ничего полезного.

Если это актуально, мои изображения имеют размер 310x316 и у меня есть 114 кадров для загрузки, анимация загружается в формате XML.

Мой класс MainMenu:

public class MainMenuActivity extends Activity{

 private String TAG = MainMenuActivity.class.getSimpleName();
 ImageView rabbitMenu ;
 AnimationDrawable ad;   

public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);        
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);               
        setContentView(R.layout.mainmenu);          
        rabbitMenu = (ImageView) findViewById(R.id.rabbitMenu);
        rabbitMenu.setImageResource(R.drawable.rabbit_menu_animation);
        ad = (AnimationDrawable) rabbitMenu.getDrawable();

    }

public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);       
        ad.start();     

}
}

Журнал ошибок:

01-31 16:13:11.320: E/AndroidRuntime(19550): FATAL EXCEPTION: main
01-31 16:13:11.320: E/AndroidRuntime(19550): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
01-31 16:13:11.320: E/AndroidRuntime(19550):    at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
01-31 16:13:11.320: E/AndroidRuntime(19550):    at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:563)
01-31 16:13:11.320: E/AndroidRuntime(19550):    at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:439)

Edit:

После предложения @OleGG, и теперь второй запуск проходит нормально, но при третьем запуске я, очевидно, пытаюсь использовать переработанное растровое изображение.

Итак, я отказался от использования AnimationDrawable и использовал AsncTask для решения своей проблемы. Теперь я продолжаю изменять фон ImageView на моем onProgressUpdate (). Возможно, не лучший подход, но сейчас у меня нет проблем! Вот мой код AsyncTask на случай, если у кого-то возникнет такая же проблема!

private class DrawRabbit extends AsyncTask<Void, Integer, Void> {
         private boolean running = true;
         private int fps = 24;
         private int frame = 10014;
         private String drawableName; 
         @Override
         protected Void doInBackground(Void... params) {


             while (running){
                 try {  
                    if (frame == 10014)
                        Thread.sleep(1000);
                    else 
                        Thread.sleep(fps);
                    if (frame < 10160) { 
                        frame++;
                        publishProgress(frame);
                    }
                    else 
                        running = false;

                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
             }               
             return null;
         }

         protected void onProgressUpdate(Integer... frame) {
             drawableName = "rm" + frame[0];
             int res_id = getResources().getIdentifier(drawableName, "drawable", getPackageName());          
             rabbitFrame.setBackgroundResource(res_id);
         }   
     }

Ответы [ 5 ]

22 голосов
/ 31 января 2012

Вам нужно освободить память, используемую растровыми изображениями, когда вы выходите из этого экрана.К сожалению, для более ранних версий Android (afaik это было «исправлено» начиная с 3.0, но я могу ошибаться) память для растровых изображений выделяется через собственный код, и, что печально, память должна быть явно освобождена.

Так, Я полагаю, вы попробуете такой подход:

  1. Переместите код для AnimationDrawable в метод onResume ().
  2. Добавьте следующий код для освобождения памяти в onPause ().

    ad.stop();
    for (int i = 0; i < ad.getNumberOfFrames(); ++i){
        Drawable frame = ad.getFrame(i);
        if (frame instanceof BitmapDrawable) {
            ((BitmapDrawable)frame).getBitmap().recycle();
        }
        frame.setCallback(null);
    }
    ad.setCallback(null);
    

Надеюсь, это поможет вам.

3 голосов
/ 28 февраля 2012

Итак, мне удается решить мою проблему следующим способом:

Создан один AsyncTask для рисования

private class DrawRabbit extends AsyncTask<Void, Integer, Void> {    
     private int fps = 24;
     private int frame = 10014;
     private String drawableName; 
     @Override
       protected Void doInBackground(Void... params) {
         while (running){
           try {  
            if (frame == 10014)
              Thread.sleep(1000);
            else 
              Thread.sleep(fps);
          if (frame < 10160) { 
            frame++;
            publishProgress(frame);
          }
          else 
            running = false;

        } catch (InterruptedException e) { 
          e.printStackTrace();
        }
         }           
         return null;
       }

       protected void onProgressUpdate(Integer... frame) {
         drawableName = "rm" + frame[0];
         int res_id = getResources().getIdentifier(drawableName, "drawable", getPackageName());        
           rabbitFrame.setBackgroundResource(res_id);
       }   
   }

и запустить его на WindowsFocusChanged

public void onWindowFocusChanged(boolean hasFocus) {    
    super.onWindowFocusChanged(hasFocus);         
    if (hasFocus)
      new DrawRabbit().execute((Void)null) ;
    else 
      running = false;
    Log.d(TAG,"onFocusChanged");
  }

Надеюсь на этопомогает кому-то!

1 голос
/ 03 апреля 2017

добавьте эти две строки в файл манифеста Android android: hardwareAccelerated = "true" и android: largeHeap = "true"

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.xxx.yyy" >
    <application
        android:allowBackup="false"
        android:fullBackupContent="false"
        android:hardwareAccelerated="true"
        android:largeHeap="true">
</application>

Надеюсь, что это поможет вам.

1 голос
/ 03 апреля 2017

просто добавьте "android: largeHeap =" true "" в тег приложения Android!:)

1 голос
/ 25 апреля 2016

Вы можете попробовать уменьшить размер изображения.В моем случае это сработало.

int[] frames = {R.drawable.frame1, R.drawable.frame2, R.drawable.frame3};
AnimationDrawable drawable = new AnimationDrawable();
    for (int fr : frames) {
        Bitmap sample = BitmapFactory.decodeResource(mActivity.getResources(), fr);
        sample = Bitmap.createScaledBitmap(sample, sample.getWidth()/4, sample.getHeight()/4, false);
        drawable.addFrame(new BitmapDrawable(mActivity.getResources(), sample), 30);
    }
...