2012-01-31 3 views
8

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

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

Поиск вокруг, я могу обнаружить, что это общая проблема в 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); 
     } 
    } 
+0

Так что это 300x300x4, около 360k на изображение. Я не уверен, как анимация управляет памятью каждого фрейма, но если для очистки памяти требуется немного времени, неудивительно, что она быстро исчерпала ее. – dmon

+0

Я создал класс для отображения анимаций из чертежей: http://stackoverflow.com/a/12519216/525319 – Yar

+0

Вы спасли мне много времени, спасибо – raja

ответ

21

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

Итак, я полагаю, вы попробовать этот подход:

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

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

Я надеюсь, что это поможет.

+2

Я больше не получаю эту ошибку, но теперь, после третьего запуска (она работает нормально во второй раз), она дает мне следующую ошибку: java.lang.RuntimeException: Canvas: пытается использовать переработанную растровую карту android.graphics .Bitmap @ 4053c370. Я добавил свой текущий код к моему вопросу, любые идеи о том, как исправить? – caiocpricci2

+0

Мне удалось решить проблему, не используя Animation Drawable, я представил свое решение в вопросе. Поскольку это решило то, что я попросил, я принимаю его как правильный ответ! Спасибо за помощь! – caiocpricci2

+0

Вы можете получить «попытку использования переработанного растрового изображения» в двух случаях: 1) Если вы не справляетесь с созданием | удалением растрового изображения правильно (например, - пытаетесь повторно использовать drawable, в то время как базовый Bitmap уже переработан). 2) На некоторых устройствах (например, LG Optimus), лежащих в основе встроенного кода для загрузки растровых изображений, как я понимаю, используется какое-то «кэширование». Это означает, что если Drawable был ранее загружен, система думает, что он все еще находится в памяти и не нуждается в перезагрузке, даже если я переработал растровое изображение. – OleGG

3

Таким образом, мне удалось решить мою проблему с этим подходом:

создал один 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); 
     } 
    } 

и запустить его onWindowsFocusChanged

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

Надеется, что это помогает кто-то!

+0

Thread.sleep() заставляет мой поток пользовательского интерфейса навсегда –

+0

Если у вас есть sleep() в вашей нити u, вы делаете это неправильно. – caiocpricci2

+0

Нет, я не сплю его в 'doInBackground()'! –

1

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

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

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

+0

one line решение. :) –

1

добавить эти две строки в файле манифеста андроида андроид: hardwareAccelerated = «истинный» и андроида: largeHeap = «истинный»

<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> 

Надежда, которая поможет вам.