9

I have a complex Battle system which has 1 parent Activity and then several child classes which access the static variables of the BattleActivity by extending Battle and passing the context to those classes.

This all seems to be working fine, however I am having an issue with releasing all my AnimationDrawables from memory. There can be a total of 24 DrawableAnimations used throughout a battle. This is ok for now, however every time the user encounters a new monster then 4 more AnimationDrawables are added to memory which will slowly but surley cause my App to Crash with anb Out of Memory exception.

Therefore, I really need to find a way to release all the memory my battle system takes up as soon as I exitit. Currently, I am testing on a Sony Z2 and when the user enters a battle the mmeory increases from 91mb to 230mb. I need to get this memory usage back down to 91mb when the battle has compelted. I have added some very basic code snippet to give you an idea of how the app currently flows and what I am trying to do to release the memory.

public class Battle extends Activity
{
     // I have several AnimationDrawables loaded into memory
     // These are then assigned the relevent animation to a button swicthing between these animations throughout my battle
    ImageButton btnChr1;
    AnimationDrawable cAnimHit1;
}

//This is the use of one of those AnimationDrawables
public class Battle_Chr_Anim extends Battle
{
    protected Context ctx;
    private ImageButton btnChr1;
    AnimationDrawable cAnimHit1;

    public Battle_Chr_Anim(Context c, ImageButton _btnChr1, AnimationDrawable _cAnimHit1) { 
        this.ctx = c;
        this.btnChr1 = _btnChr1;
        this.cAnimHit1 = _cAnimHit1;
    }

    // Bound the ImageButton
    int id = ctx.getResources().getIdentifier("idle", "drawable", ctx.getPackageName());
    img_chr1.setBackgroundResource(id);
    frameAnimation = (AnimationDrawable)img_chr1.getBackground();
    frameAnimation.start()

    // Loaded into memory ready so I can swicth them over quickly when user attacks
    int ca1 = ctx.getResources().getIdentifier("attack", "drawable", ctx.getPackageName());
    cAnimHit1 = (AnimationDrawable)chrHit1.getBackground();
    cAnimHit1.start();
}

public class Battle_Ended extends Battle
{
    protected Context ctx;

    public Battle_Ended(Context c) { 
        this.ctx = c;
    }

    //This is a dialog popup when the user completes the battle closing the battle activty
    void EndBattle()
    {
        ImageButton btnSubmit = (ImageButton)dialog.findViewById(R.id.imgBtnSubmit);
        btnSubmit.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent returnIntent = new Intent();
                setResult(RESULT_CANCELED, returnIntent);
                RecyleAllAnimations();
                dialog.dismiss();
                ((Activity) ctx).finish();
            }
        });
    }

    void RecyleAllAnimations()
    {
        // I want all AnimationDrawables from memory here, I believe the below line removes the one currently in use, however I have no way of releasing the other animations sitting in memory.
        img_chr1.setBackgroundResource(android.R.color.transparent);
        System.gc();
    }
}
SingleWave Games
  • 2,618
  • 9
  • 36
  • 52
  • Do you really have static variables which you pass the application context? i think they are going to kill you app much earlier than your drawables – MineConsulting SRL Nov 07 '14 at 14:37
  • Yes this is only for my Battle side of things, as I did not want all my code in the same Activity and simply extend this class so that I can still access the UI. As i need to be able to load up all the AnimationDrawables before I load up the Activity so that the animations can be fluent when the user enters a battle – SingleWave Games Nov 07 '14 at 14:43

2 Answers2

0

first of all I think you can not do any recycling for AnimationDrawable unless you leave the scope that they are declared. do not relay on System.gc(); and let GC automatically releases those memory for you.

which access the static variables of the BattleActivity

this is your first mistake. If you use static variable even if you leave your activity they are all there and you will definitely get OOM.

Therefore, I really need to find a way to release all the memory my battle system takes up as soon as I exitit.

if you declare all variables as non static variables then you wont get OOM when you leave the scope they are defined. although you may get OOM if you create AnimationDrawable without any bounds during activity is running.

mmlooloo
  • 18,937
  • 5
  • 45
  • 64
  • I have amended my code so that I am no longer using static AnimationDrawable, however am still having an issue of releasing the memory even after the activity is destroyed and the user returned to the Game Map page. Any other ideas? – SingleWave Games Nov 10 '14 at 14:26
  • what issue do you have now? – mmlooloo Nov 10 '14 at 18:36
  • Well simply put the memory is not being released for the AnimatonDrawables – SingleWave Games Nov 10 '14 at 19:58
  • I am not sure but I think as you leave that activity gc dose not run immediately, I think it only runs when you need more memory and dose not have enough, so play around and see if it will do any cleaning or not. – mmlooloo Nov 10 '14 at 20:29
0

Using static Drawable(s) or View(s) will cause a memory leak because drawables are stateful.

You could either check for any other static Drawable(s) or View(s) and get rid of them or call

drawable.setCallback(null);

You cannot recycle Drawable objects and it is not a good practice to call System.gc()

It seems that your app is suffering from a memory leak somewhere, if you tried the above and memory still expands at that rate then consider doing memory analysis to narrow down the problem.

Mostafa Gazar
  • 2,487
  • 18
  • 23
  • I don't think it is the fact that I am suffering from memory leak, I think its the shear amount of memory the drawable use. I mean on one load I have 30 AnimationDrawable that need to be loaded up, each consists of roughly 15 images each 256px x 256px. So as expected the memory usage shoots up (It also takes 2-4 seconds to load based on the device). This i can deal with, however ones this is done I need to somehow release that memory. As each time a new monster is encountered a further 5 AnimationDrawables are added to memory – SingleWave Games Nov 13 '14 at 11:17
  • That's true but if your Activity is not leaked, memory should be released as needed, also if you called System.gc() manually after closing the Activity those Drawable(s) should be cleared off memory unless there is a leak. What if beside making sure no static Drawable(s) are used and the other things I mentioned make them point to null upon finish to avoid possible Loitering – Mostafa Gazar Nov 13 '14 at 11:33
  • I think i will have to look into this tonight as need to remove static from all ImageViews and ImageButtons then can test it properly. What is the vest way to check for a memory leak and memory analysis tool? – SingleWave Games Nov 13 '14 at 13:15
  • If your memory usage is always going up that indicates a memory leak, check these links for how to find the leaks, http://android-developers.blogspot.com/2011/03/memory-analysis-for-android.html and https://developer.android.com/tools/debugging/debugging-memory.html – Mostafa Gazar Nov 13 '14 at 13:21
  • Having a static ImageView for example will cause the whole Activity to be leaked (stay on memory), you should get rid of all these static View(s) and Drawable(s) – Mostafa Gazar Nov 13 '14 at 13:22
  • Yes I am now working on that, is this the same for all views including things like a TextView? – SingleWave Games Nov 13 '14 at 13:27
  • Yes all views including TextView(s) – Mostafa Gazar Nov 13 '14 at 13:30