My Android application’s gameLoop thread crashes on exit
The problem is that when I am in the game and click on the home button, the gameLoop thread (I guess) gets confused and then pops up “Unfortunately, ‘app name’ has stopped. ”
I wrote a very simple program to solve this problem. What this program does is display a number on the screen and increment it when you touch the screen.
When I comment out view.onDraw(c) in GameLoopThread, everything seems to work fine.
Error message:
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)
The code is as follows:
The main activity .java
package com.example.crashtest;
import android.os.Bundle;
import android.app.Activity;
import android.content.res.Configuration;
import android.view.KeyEvent;
import android.view.Window;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(new GameView(this));
}
@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:
return true;
case KeyEvent.KEYCODE_VOLUME_DOWN:
return true;
default:
return false;
}
}
}
Game View .java
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);
}
}
Game loop thread .java
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) {}
}
}
}
Solution
When you close the activity
to see what you need, you need to stop your thread from my code to see it:
GameView gameView ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
gameView = new GameView(this);
setContentView(gameView);
}
@Override
protected void onPause() {
TODO Auto-generated method stub
super.onPause();
gameView.StopView();
}
And you need to implement a method called StopView()
in your GameView
like this
public void StopView() {
if (gameLoopThread != null) {
gameLoopThread.setRunning(false);
}
}
The problem is because you still call the run method in the thread after trying to block it, so you need to stop it before blocking it with a join.
I tested your code with the new implementation and it worked well for me, feedback to me