Animation Drawable causing OutOfMemoryError on second run in Android

12,018

Solution 1

You need to free memory used by bitmaps, when you're exiting this screen. Unfortunately, for earlier versions of Android (afaik this was "fixed" since 3.0, but I can be wrong) memory for bitmaps is allocated through native code and, what is sad, memory should be freed explicitly.

So, I suppose you to try this approach:

  1. Move code for AnimationDrawable to onResume() method.
  2. Add following code for releasing memory to 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);
    

I hope it'll help you.

Solution 2

So, I manage to solve my problem with this approach:

Created one AsyncTask for drawing

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

and start it onWindowsFocusChanged

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

Hope it helps someone!

Solution 3

You can try by reducing the size if images. My case it worked.

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

Solution 4

just add "android:largeHeap="true"" in application tag android manifest! :)

Solution 5

add these two lines in your android manifest file android:hardwareAccelerated="true" and 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>

Hope that will help you.

Share:
12,018
caiocpricci2
Author by

caiocpricci2

Updated on June 09, 2022

Comments

  • caiocpricci2
    caiocpricci2 almost 2 years

    I am currently developing a game and I'm trying to use one AnimationDrawable to show one animation in my Main Menu. When I run the game once, it works perfectly. But if i tap the back button, when I open the game again it gives me OutOfMemorryError. If I hit home and go back to the game, it loads but doesn't show the animation, it's empty where it should be.

    I think when it opens the game again it tries to load the animation, but it's already loaded from the previous run, can i free that memory somehow? How can I treat that exception?

    Searching around I can find that its a common problem in Android, but I couldn't find anything useful.

    If it's relevant, my images are 310x316 and I have 114 frames to load, the animation is loaded in a xml.

    My MainMenu class:

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

    The error log:

    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:

    After @OleGG suggestion and now the second run goes fine, but on the third run I'm apparently trying to use a recycled bitmap.

    So I gave up of using AnimationDrawable, and I used AsncTask to solve my problem. Now i keep changing the background of the ImageView on my onProgressUpdate(). May not be the best approach, but now I'm not having any problems! Here is my AsyncTask code in case someone has the same issue!

    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);
             }   
         }
    
  • caiocpricci2
    caiocpricci2 over 12 years
    I'm not getting that error anymore, but now, after the third run(it runs fine in the second time) it gives me the following error : java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@4053c370. I've added my current code to my question, any ideas about how to fix?
  • caiocpricci2
    caiocpricci2 over 12 years
    I managed to solve the problem by not using Animation Drawable, I provided my solution in the question. Since this solved what I asked I'm accepting it as correct answer! Thanks for the help!
  • OleGG
    OleGG over 12 years
    You can get "trying to use a recycled bitmap" in two cases: 1) If you don't handle creation|deletion of bitmap correctly (for example - trying to reuse drawable, while underlying Bitmap is already recycled). 2) On some devices (LG Optimus, for example) underlying native code for bitmap loading, as I understand, using some sort of "caching". It means that if Drawable was previously loaded, system thinks, that it is still in memory and doesn't need to be reloaded, even if I recycled the bitmap.
  • caiocpricci2
    caiocpricci2 over 10 years
    If you have a sleep() in your ui thread you're doing it wrong.
  • Muhammad Babar
    Muhammad Babar over 10 years
    No i'm not i'm sleeping it in doInBackground()!
  • Harsh Parikh
    Harsh Parikh about 9 years
    thanks , it worked for me.I have used it on destroy and it worked for me
  • Hitesh Dhamshaniya
    Hitesh Dhamshaniya about 7 years
    one line solution. :)
  • Sukitha Udugamasooriya
    Sukitha Udugamasooriya over 5 years
    Answered very well. Works for me.
  • ConcernedHobbit
    ConcernedHobbit about 4 years
    You sir are a life saver! My fragments were not releasing my animation bitmaps on exiting, and after switching between and opening multiple fragments my app would run out of memory. Now it works!