1

My app runs fine on first run. Then when I terminate it and run it again, it just brings up the force close window. After I force close the app, it runs fine again. The app alternates between force close and runs fine.

am I missing some cleanup before the app terminates?

following is some of my code (contains three classes, foobar,actionview & actionthread:

foobar.java:

public class foobar extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(new ActionView(this));
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    @Override
    protected void onPause() {
        super.onPause();
    }

    @Override
    protected void onStop() {
        super.onStop();
    }
}

public class ActionView extends SurfaceView implements SurfaceHolder.Callback {

    private ActionThread actionThread;

    public ActionView(Context context) 
    {
        super(context);
        getHolder().addCallback(this);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) 
    {
        actionThread = new ActionThread(holder,this);
        actionThread.setRunning(true);
        actionThread.start();
        }


    @Override
    public void surfaceDestroyed(SurfaceHolder holder) 
    {
        boolean retry = true;
        while(retry)
        {
            try
            {
                actionThread.join();
                retry=false;
            }
            catch(InterruptedException e)
            {
            }
        }
    }

    protected void displayGameState(Canvas canvas) 
    {
        //code...
        }

        public void updateGameState() 
    {
        //code.. 
         if something happens then
    actionThread.setRunning(false);
    ((Activity)getContext()).finish();

        }
}

ActionThread.java:

public class ActionThread extends Thread {

    private boolean threadIsRunning;
    private SurfaceHolder surfaceHolder;
    private ActionView actionView;
    private final static int MAX_FPS = 50;
    private final static int MAX_FRAME_SKIPS = 5;
    private final static int FRAME_PERIOD = 1000/MAX_FPS;
    private static final String TAG = ActionThread.class.getSimpleName();

    public ActionThread(SurfaceHolder holder, ActionView actionView) {
        this.actionView= actionView;
        surfaceHolder = holder;
    }

    public void setRunning(boolean running)
    {
        threadIsRunning = running;
    }

    @Override
    public void run() 
    {
        long tickCount = 0L;
        long totalFramesSkipped = 0L;
        Canvas canvas = null;
        long beginTime; //time the cycle began
        long timeDiff;  //time it took for cycle to execute
        int sleepTime; //milliseconds to sleep (< 0 if time it took to complete cycle is longer than FRAME_PERIOD
        int framesSkipped; //# of frames being skipped

        while(threadIsRunning)
        {
            tickCount++;
            try
            {
                canvas = surfaceHolder.lockCanvas();
                synchronized(surfaceHolder)
                {
                    beginTime = System.currentTimeMillis();
                    framesSkipped = 0;

                    actionView.updateGameState();
                    actionView.displayGameState(canvas);

                    //calc how long the cycle took
                    timeDiff = System.currentTimeMillis() - beginTime;
                    //calc sleep time
                    sleepTime = (int)(FRAME_PERIOD - timeDiff);

                    if(sleepTime > 0)
                    {
                        //if sleepTime > 0 , didn't fall behind. Try to sleep
                        try
                        {
                            Thread.sleep(sleepTime);
                        }
                        catch(InterruptedException e){}
                    }
                    while(sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS)
                    {   //fell behind and need to catch up (update without displaying)
                        actionView.updateGameState();
                        sleepTime+=FRAME_PERIOD; //add frame period to check if in next frame
                        framesSkipped++;
                        totalFramesSkipped++;
                    }
                }
            }
            finally
            {
                if(canvas != null)
                {
                    surfaceHolder.unlockCanvasAndPost(canvas);
                }
            }
        }
    }

}

Here is the logcat error:

05-03 22:47:45.449: D/dalvikvm(30596): GC_EXTERNAL_ALLOC freed <1K, 43% free 3126K/5447K, external 36955K/38794K, paused 32ms
05-03 22:47:45.488: I/dalvikvm-heap(30596): Clamp target GC heap from 43.427MB to 42.000MB
05-03 22:47:45.488: D/dalvikvm(30596): GC_FOR_MALLOC freed 0K, 43% free 3126K/5447K, external 37165K/38794K, paused 18ms
05-03 22:47:45.535: D/dalvikvm(30596): GC_EXTERNAL_ALLOC freed <1K, 43% free 3126K/5447K, external 37165K/38794K, paused 29ms
05-03 22:47:45.574: I/dalvikvm-heap(30596): Clamp target GC heap from 43.674MB to 42.000MB
05-03 22:47:45.582: D/dalvikvm(30596): GC_FOR_MALLOC freed 0K, 43% free 3126K/5447K, external 37418K/38794K, paused 18ms
05-03 22:47:45.613: D/dalvikvm(30596): GC_EXTERNAL_ALLOC freed <1K, 43% free 3126K/5447K, external 37244K/38794K, paused 30ms
05-03 22:47:45.652: I/dalvikvm-heap(30596): Clamp target GC heap from 43.804MB to 42.000MB
05-03 22:47:45.652: D/dalvikvm(30596): GC_FOR_MALLOC freed 0K, 43% free 3126K/5447K, external 37551K/38794K, paused 17ms
05-03 22:47:45.684: D/dalvikvm(30596): GC_EXTERNAL_ALLOC freed <1K, 43% free 3126K/5447K, external 37551K/38794K, paused 27ms
05-03 22:47:45.699: E/dalvikvm-heap(30596): 258908-byte external allocation too large for this process.
05-03 22:47:45.723: I/dalvikvm-heap(30596): Clamp target GC heap from 43.804MB to 42.000MB
05-03 22:47:45.723: D/dalvikvm(30596): GC_FOR_MALLOC freed <1K, 43% free 3126K/5447K, external 37551K/38794K, paused 17ms
05-03 22:47:45.723: D/GraphicsJNI(30596): Waiting for heap walker to free more memory
05-03 22:47:45.723: D/GraphicsJNI(30596): Heap walker done, retry to allocate
05-03 22:47:45.754: D/dalvikvm(30596): GC_EXTERNAL_ALLOC freed <1K, 43% free 3126K/5447K, external 37298K/38794K, paused 27ms
05-03 22:47:45.793: I/dalvikvm-heap(30596): Clamp target GC heap from 43.804MB to 42.000MB
05-03 22:47:45.793: D/dalvikvm(30596): GC_FOR_MALLOC freed 0K, 43% free 3126K/5447K, external 37551K/38794K, paused 17ms
05-03 22:47:45.832: D/dalvikvm(30596): GC_EXTERNAL_ALLOC freed <1K, 43% free 3126K/5447K, external 37551K/38794K, paused 26ms
05-03 22:47:45.840: E/dalvikvm-heap(30596): 313968-byte external allocation too large for this process.
05-03 22:47:45.871: I/dalvikvm-heap(30596): Clamp target GC heap from 43.804MB to 42.000MB
05-03 22:47:45.871: D/dalvikvm(30596): GC_FOR_MALLOC freed 0K, 43% free 3126K/5447K, external 37551K/38794K, paused 17ms
05-03 22:47:45.871: D/GraphicsJNI(30596): Waiting for heap walker to free more memory
05-03 22:47:45.871: D/GraphicsJNI(30596): Heap walker done, retry to allocate
05-03 22:47:45.902: D/dalvikvm(30596): GC_EXTERNAL_ALLOC freed <1K, 43% free 3126K/5447K, external 37551K/38794K, paused 30ms
05-03 22:47:45.910: E/dalvikvm-heap(30596): 313968-byte external allocation too large for this process.
05-03 22:47:45.941: I/dalvikvm-heap(30596): Clamp target GC heap from 43.804MB to 42.000MB
05-03 22:47:45.941: D/dalvikvm(30596): GC_FOR_MALLOC freed 0K, 43% free 3126K/5447K, external 37551K/38794K, paused 17ms
05-03 22:47:45.941: E/GraphicsJNI(30596): VM won't let us allocate 313968 bytes
05-03 22:47:45.957: D/AndroidRuntime(30596): Shutting down VM
05-03 22:47:45.957: W/dalvikvm(30596): threadid=1: thread exiting with uncaught exception (group=0x4001e560)
05-03 22:47:45.965: E/AndroidRuntime(30596): FATAL EXCEPTION: main
05-03 22:47:45.965: E/AndroidRuntime(30596): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
05-03 22:47:45.965: E/AndroidRuntime(30596):    at android.graphics.Bitmap.nativeCreate(Native Method)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at android.graphics.Bitmap.createBitmap(Bitmap.java:507)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at android.graphics.Bitmap.createBitmap(Bitmap.java:474)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:379)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at com.somecomany.foobar.Element.Element.resizeBitmap(Element.java:48)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at com.somecomany.foobar.Element.Element.<init>(Element.java:31)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at com.somecomany.foobar.Element.MobileElement.<init>(MobileElement.java:24)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at com.somecomany.foobar.ActionView.surfaceCreated(ActionView.java:156)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at android.view.SurfaceView.updateWindow(SurfaceView.java:543)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at android.view.SurfaceView.dispatchDraw(SurfaceView.java:348)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at android.view.ViewGroup.drawChild(ViewGroup.java:1644)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at android.view.View.draw(View.java:6883)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at android.widget.FrameLayout.draw(FrameLayout.java:357)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at android.view.ViewGroup.drawChild(ViewGroup.java:1646)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at android.view.View.draw(View.java:6883)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at android.widget.FrameLayout.draw(FrameLayout.java:357)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:2106)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at android.view.ViewRoot.draw(ViewRoot.java:1562)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at android.view.ViewRoot.performTraversals(ViewRoot.java:1298)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at android.view.ViewRoot.handleMessage(ViewRoot.java:1911)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at android.os.Handler.dispatchMessage(Handler.java:99)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at android.os.Looper.loop(Looper.java:130)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at android.app.ActivityThread.main(ActivityThread.java:3821)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at java.lang.reflect.Method.invokeNative(Native Method)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at java.lang.reflect.Method.invoke(Method.java:507)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
05-03 22:47:45.965: E/AndroidRuntime(30596):    at dalvik.system.NativeStart.main(Native Method)
05-03 22:47:49.996: I/Process(30596): Sending signal. PID: 30596 SIG: 9
reach4thelasers
  • 26,181
  • 22
  • 92
  • 123
  • my guess is that its not stopping the surfaceview loop and that its not unlocking the canvas because of that. make sure to stop the surfaceview in your ondestroy of your activity – L7ColWinters May 04 '12 at 02:24
  • We need the logcat output to determine what is wrong. – Alex Lockwood May 04 '12 at 02:45
  • L7ColWinters - what code should I use to stop the surfaceview loop? the loop is stopped by calling actionthread.setrunning(false). Then ((activity)getcontext()).finish(). I believe this calls surfaceDestroyed() method which calls actionthread.join(). Should I have something in the onstop()? – newyorkgena May 04 '12 at 03:19

2 Answers2

1

From the log, it looks like you have a memory leak while creating bitmaps, which happens when you have some large sized Objects hanging around, perhaps through static data structures when the application is run the second time.

Another variant scenario is when you reference the Context or Activity in a static variable, which basically would prevent the previous Activity from being garbage collected.

This happens because when you "exit" an app in Android, its process is not necessarily terminated. So that when you run it again, the OS can skip some time-consuming steps, and the app can be launched faster. This is good when you keep some of your data around for quick reuse, and this is very bad when you inadvertently keep some of your (huge) data around and then try to recreate those data again, that's when things go boom.

Kai
  • 15,284
  • 6
  • 51
  • 82
  • how do you guarantee the destruction of all objects (regardless of whether they are static or not)? like some sort of simple command perhaps placed in onPause method? I tried setting activity:launchMode to SingleTop in manifest as Changwei suggests below, but that didn't have the desired effect. – newyorkgena May 05 '12 at 01:00
  • Kai, I removed the word static from my app. Thus making any static variables non-static. Unfortunately, it still gives the same error. – newyorkgena May 05 '12 at 05:40
  • activity:launchMode has nothing to do with memory management. To resolve this you need to use memory profiling to check out where the memory leak(s) are. Check this: http://stackoverflow.com/questions/3112671/android-memory-analysing-eclipse-memory-analyzer and this: http://android-developers.blogspot.com/2011/03/memory-analysis-for-android.html – Kai May 05 '12 at 17:02
  • I ran the app through the memory analyzer. my app did not have any large allocations. org.bouncycastle had much more objects with high allocations in shallow heap. I did manage to pinpoint where the problem is. I have a 2-D arraylist that holds bitmaps that are retrieved from res/drawable-hdpi folder using BitmapFactory.decodeResource(). If I reduce the number of bitmaps allocated, the error goes away. The more bitmaps I allocate, the more of these messages I get in LogCat: GC_EXTERNAL_ALLOC freed... How do I alter this process of bitmap allocation? what am I doing wrong? – newyorkgena May 08 '12 at 01:13
  • GC_EXTERNAL_ALLOC means that the Dalvik VM is trying to free more collectible objects (i.e. normal Java Objects) to make room for non collectible objects (your Bitmaps). You should be alarmed if you see this a lot, it means that your app will experience frequent pauses, and if you ever allocate more Objects than you anticipated, your app will run out of memory (and crash). Remember you are developing for mobile devices with limited memory, so there's nothing you can do but to strike a better balance between caching bitmaps and make room available for other stuff. – Kai May 08 '12 at 02:32
  • but total size of the bitmaps that I allocate totals to about 2megs at this point. And this is done in the beginning of the app. This is done in the sufraceCreated method of the surfaceView class. Which is called only once. Once the bitmaps are allocated, they are just redisplayed over and over on the screen. 2megs cant be too much for a game size. so am I supposed to constantly allocate and de-allocate during the running of the app? what is the safe maximum that one should load into memory at any one time? or am i doing this in the wrong method? should I be doing the allocating in constuctor? – newyorkgena May 08 '12 at 13:04
  • Hmmm 2MB isn't too much, is your game currently negatively effected? Does your app still crashes now? – Kai May 08 '12 at 15:13
  • It crashes on beginning of every other game run if I load the current 2 megs of bitmaps (exactly as I described at the top of this page). If I get rid of roughly half the bitmaps, then it no longer crashes. But you can still see the GC_EXTERNAL_ALLOC freed messages (although fewer of them). The less bitmaps I do, the less of those GC_EXTERNAL.... messages I get in LogCat. If I get rid of 75% of my bitmap allocation, then I will get none of those messages. The problem is that the current bitmaps I have isn't even 15% of what I eventually plan to use in the game. Something is wrong. – newyorkgena May 09 '12 at 02:15
  • Please see comment before this one. Other than the crash that occurs every other game start, the game twitches (speeds up) at random times. Does it more often when I press the screen, causing onTouchEvent to be processed. But that may be due to my handling of the game loop (shown above in ActionThread class). – newyorkgena May 09 '12 at 02:22
  • Another thing to try is to set as many of your fields to null in Activity's onDestory method. Since Android would create a new Activity first before release the older Activity for garbage collection, this should help. – Kai May 09 '12 at 02:48
  • thanks. How do I prevent those GC_EXTERNAL_ALLOC messages in the first place? is using BitmapFactory.decodeResource() for storing bitmaps not a good method? – newyorkgena May 09 '12 at 03:06
  • I don't think you can remove them completely, and they are not necessary indications of error. – Kai May 09 '12 at 06:38
  • using clear() method on the arraylist that holds the bitmaps worked!. I no longer get the crash on running every other time. Thank you Kai – newyorkgena May 11 '12 at 03:59
0

Try to set the Activity's LaunchMode to SingleTop.

And you should release the resources when the Activity onDestroy. good luck.

Changwei Yao
  • 13,051
  • 3
  • 25
  • 22