I've struggled with this for some months now. I've just been ignoring it, because it hasn't been to much of a problem to me, but now I'm ready for release, and would like to fix it up.
The problem is that when I'm in the game, and click the Home button, the gameLoop thread (I guess) fucks up and then it pops up "Unfortunately, <app name> has stopped."
It also crashes when I rotate the screen (not too much of a problem, because I have fixed orientation, but would like to fix it for future projects)
I have very little experience with Java and Android development, so please keep that in mind.
I've made a very simple program where this problem occurs. All the program does is showing a number on the screen, and increasing it when you touch the screen.
Error message:
Code: Select all
FATAL EXEPTION: Thread-23207
java.lang.NullPointerExeption
at com.example.crashtest.GameView.onDraw(GameView.java:61)
at com.example.crashtest.GameLoopThread.run(GameLoopThread.java:34)
MainActivity.java
Code: Select all
package com.example.crashtest;
import android.media.AudioManager;
import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.view.KeyEvent;
import android.view.Window;
public class MainActivity extends Activity {
private AudioManager am;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(new GameView(this));
am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
return true;
case KeyEvent.KEYCODE_MENU:
return true;
case KeyEvent.KEYCODE_VOLUME_UP:
am.adjustStreamVolume(AudioManager.STREAM_MUSIC,
AudioManager.ADJUST_RAISE, AudioManager.FLAG_SHOW_UI);
return true;
case KeyEvent.KEYCODE_VOLUME_DOWN:
am.adjustStreamVolume(AudioManager.STREAM_MUSIC,
AudioManager.ADJUST_LOWER, AudioManager.FLAG_SHOW_UI);
return true;
default:
return false;
}
}
}
Code: Select all
package com.example.crashtest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class GameView extends SurfaceView {
private SurfaceHolder holder;
private GameLoopThread gameLoopThread;
private int num = 0;
public GameView(final Context context) {
super(context);
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
gameLoopThread.setRunning(false);
while (retry) {
try {
gameLoopThread.join();
retry = false;
} catch (InterruptedException e) {}
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
makeThread();
gameLoopThread.start();
gameLoopThread.setRunning(true);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
});
}
private void makeThread() {
gameLoopThread = new GameLoopThread(this);
}
@SuppressLint({ "WrongCall", "DrawAllocation" })
@Override
protected void onDraw(Canvas canvas) {
// Draw black background - Write variable 'num' on the screen
canvas.drawColor(Color.BLACK);
Paint paint = new Paint();
paint.setARGB(255, 0, 255, 0);
paint.setTextSize(50);
paint.setTextAlign(Align.CENTER);
canvas.drawText(Integer.toString(num), getWidth() / 2, getHeight() / 2, paint);
}
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN) {
// Increase 'num' with 1 every touch
num++;
}
return super.onTouchEvent(event);
}
}
Code: Select all
package com.example.crashtest;
import android.annotation.SuppressLint;
import android.graphics.Canvas;
public class GameLoopThread extends Thread {
static final long FPS = 10;
private GameView view;
public boolean running = false;
public boolean pause = false;
public GameLoopThread(GameView view) {
this.view = view;
}
public void setRunning(boolean run) {
running = run;
}
@SuppressLint("WrongCall")
@Override
public void run() {
long ticksPS = 1000 / FPS;
long startTime = 0;
long sleepTime = 0;
while (running) {
Canvas c = null;
startTime = System.currentTimeMillis();
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {
view.onDraw(c);
}
} finally {
if (c != null) {
view.getHolder().unlockCanvasAndPost(c);
}
}
sleepTime = ticksPS-(System.currentTimeMillis() - startTime);
try {
if (sleepTime > 0)
sleep(sleepTime);
else
sleep(10);
} catch (Exception e) {}
}
}
}