Search code examples
javaandroidlogcatonpause

Android App Crashes onPause() -- Logic Works in One Project Fails in Another


I before I started the game I'm currently working on, I built the following logic to handle pausing. Everything works perfectly in there, or so I thought. In the test app that logic still works, which means I broke it somewhere else along the line. Based on the logic below and the LogCat messages, can you point me in the direction that I should be looking? I don't know how to interpret the errors below. Any help would be very welcome. I can't exactly require the game to be restarted if it ever goes to sleep.

Thanks!

MainGamePanel.java

public void pause() {
    mSensorManager.unregisterListener(this);

}

public void resume(Context context) {
    mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_FASTEST);  
}

public void destroy() {
    thread.setRunning(false);

    if (thread != null)
    {
        Thread killThread = thread;
        thread = null;
        killThread.interrupt();
    }   

}

@Override
public void surfaceCreated(SurfaceHolder holder) {
    //continue the thread
    synchronized (thread) {
        thread.pleaseWait = false;
        thread.notifyAll();
    }

}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    //pause the thread
    synchronized (thread) {
        thread.pleaseWait = true;
    }

}

Main Activity:

//Restarts the accelerometer after onPause
protected void onResume() {
    super.onResume();
    viewPanel.resume(this);

}

//Standard Method run when the Application loses focus.
//This runs the pause() function in the viewPanel so that
//the accelerometer can be paused.
protected void onPause() {
    super.onPause();   
    viewPanel.pause();

}

protected void onDestroy() {
    super.onDestroy();
    viewPanel.destroy();
}

@Override
public boolean onKeyDown(int keyCode, KeyEvent event)  {
    if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
        // do something on back.
        viewPanel.backHit();
        return true;
    }

    return super.onKeyDown(keyCode, event);
}

And my MainThread.java file:

    package com.petronicarts.stormthecastle;

import com.petronicarts.stormthecastle.MainGamePanel;

import android.graphics.Canvas;
import android.graphics.Matrix;
import android.view.SurfaceHolder;

public class MainThread extends Thread {

private SurfaceHolder surfaceHolder;
private MainGamePanel gamePanel;
private boolean running;
public boolean pleaseWait = true;
public void setRunning(boolean running) {
    this.running = running;
}

public MainThread(SurfaceHolder surfaceHolder, MainGamePanel gamePanel) {
    super();
    this.surfaceHolder = surfaceHolder;
    this.gamePanel = gamePanel;
}

@Override
public void run() 
{
    Canvas canvas;

    Matrix matrix = new Matrix();
    matrix.preScale(gamePanel.getScaleX(), gamePanel.getScaleY());
    long startTime, elapsedTime;
    startTime = System.currentTimeMillis();
    elapsedTime = System.currentTimeMillis() - startTime;

    while (running) {
        if(!pleaseWait) {
            canvas = null;

            // try locking the canvas for exclusive pixel editing on the surface
            try {
                canvas = this.surfaceHolder.lockCanvas();
                canvas.setMatrix(matrix);
                synchronized (surfaceHolder) {
                    // update game state
                    startTime = System.currentTimeMillis();
                    this.gamePanel.update(elapsedTime);

                    // draws the canvas on the panel
                    this.gamePanel.onDraw(canvas);

                    elapsedTime = System.currentTimeMillis() - startTime;

                }
            } finally {
                // in case of an exception the surface is not left in
                // an inconsistent state
                if (canvas != null) {
                    surfaceHolder.unlockCanvasAndPost(canvas);
                }
            }   // end finally            
        }
        else {
            synchronized (this) {
                try {
                    wait();
                } catch (Exception e) { }
            }
        }
    }
}
}

The LogCat Errors:

06-07 15:56:45.852: E/AndroidRuntime(3374):     FATAL EXCEPTION: main
06-07 15:56:45.852: E/AndroidRuntime(3374):     java.lang.NullPointerException
06-07 15:56:45.852: E/AndroidRuntime(3374):     at com.petronicarts.stormthecastle.MainGamePanel.surfaceDestroyed(MainGamePanel.java:312)
06-07 15:56:45.852: E/AndroidRuntime(3374):     at android.view.SurfaceView.reportSurfaceDestroyed(SurfaceView.java:600)
06-07 15:56:45.852: E/AndroidRuntime(3374):     at android.view.SurfaceView.updateWindow(SurfaceView.java:486)
06-07 15:56:45.852: E/AndroidRuntime(3374):     at android.view.SurfaceView.onWindowVisibilityChanged(SurfaceView.java:213)
06-07 15:56:45.852: E/AndroidRuntime(3374):     at android.view.View.dispatchDetachedFromWindow(View.java:6232)
06-07 15:56:45.852: E/AndroidRuntime(3374):     at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:1248)
06-07 15:56:45.852: E/AndroidRuntime(3374):     at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:1248)
06-07 15:56:45.852: E/AndroidRuntime(3374):     at android.view.ViewRoot.dispatchDetachedFromWindow(ViewRoot.java:1868)
06-07 15:56:45.852: E/AndroidRuntime(3374):     at android.view.ViewRoot.doDie(ViewRoot.java:2958)
06-07 15:56:45.852: E/AndroidRuntime(3374):     at android.view.ViewRoot.die(ViewRoot.java:2928)
06-07 15:56:45.852: E/AndroidRuntime(3374):     at android.view.WindowManagerImpl.removeViewImmediate(WindowManagerImpl.java:254)
06-07 15:56:45.852: E/AndroidRuntime(3374):     at android.view.Window$LocalWindowManager.removeViewImmediate(Window.java:445)
06-07 15:56:45.852: E/AndroidRuntime(3374):     at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3201)
06-07 15:56:45.852: E/AndroidRuntime(3374):     at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3306)
06-07 15:56:45.852: E/AndroidRuntime(3374):     at android.app.ActivityThread.access$1600(ActivityThread.java:132)
06-07 15:56:45.852: E/AndroidRuntime(3374):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1055)
06-07 15:56:45.852: E/AndroidRuntime(3374):     at android.os.Handler.dispatchMessage(Handler.java:99)
06-07 15:56:45.852: E/AndroidRuntime(3374):     at android.os.Looper.loop(Looper.java:150)
06-07 15:56:45.852: E/AndroidRuntime(3374):     at android.app.ActivityThread.main(ActivityThread.java:4312)
06-07 15:56:45.852: E/AndroidRuntime(3374):     at java.lang.reflect.Method.invokeNative(Native Method)
06-07 15:56:45.852: E/AndroidRuntime(3374):     at java.lang.reflect.Method.invoke(Method.java:507)
06-07 15:56:45.852: E/AndroidRuntime(3374):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:849)
06-07 15:56:45.852: E/AndroidRuntime(3374):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:607)
06-07 15:56:45.852: E/AndroidRuntime(3374):     at dalvik.system.NativeStart.main(Native Method)

Solution

  • You should probably not synchronize on the thread variable (doing that in surfaceDestroyed). It's set to null elsewhere (in destroy).

    Actually, in general, it's always a bad idea to synchronize on a non final object reference.