0

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)
Nathan Tornquist
  • 6,468
  • 10
  • 47
  • 72

1 Answers1

1

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.

Mattias Isegran Bergander
  • 11,811
  • 2
  • 41
  • 49
  • That kept me from returning to the home screen when I unlocked my phone, but the app restarted. Is there an easy way to save the state? – Nathan Tornquist Jun 07 '12 at 21:47
  • Can you also explain how that doesn't break more of it? If the thread is not paused I thought that it would keep running and the app would break itself because the thread tried to process while the app was paused? – Nathan Tornquist Jun 07 '12 at 22:18
  • With that change, I am no longer able to close the application and then return to it and have the game still be in the same state. Doing it that way causes the game to crash when the home button is pressed. – Nathan Tornquist Jun 08 '12 at 14:27
  • I didn't say you shouldn't make the thread stop executing, I said you shouldn't synchronize on a non final field. – Mattias Isegran Bergander Jun 09 '12 at 19:20
  • Can you explain why? The app crashes when I try to return to the home screen when I don't synchronize. It seems better to do so. – Nathan Tornquist Jun 11 '12 at 14:11
  • Well you get a NullPointerException in `surfaceDestroyed` when you later try to synchronize on the thread field when it has been set to `null` (probably in `onDestroy`). Just synxhronize on something else. Susch as a Another field: `private final Object lock = new Object();` and use `synchronize (lock)` instead of `thread`. – Mattias Isegran Bergander Jun 11 '12 at 20:44
  • Okay. That makes sense. Why is a bad idea to synchronize on a non final object reference though? – Nathan Tornquist Jun 11 '12 at 21:02
  • Beacuase then you can assign the mutex reference and then `synchronize (x)` will not protect that part of the code as `x` can be different values in different threads (for example due to the memory model). Or in your case, even `null` and it crashes. You would've never had that problem if you only synchronize on a final reference. – Mattias Isegran Bergander Jun 11 '12 at 21:09