My Android app's gameLoop thread crashes at exit
android, java, multithreading
Solution
Look what you need when you close the `Activity` you need to stop your thread from my code implemented before 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 method called `StopView()` in your `GameView` like this
public void StopView() {
if (gameLoopThread != null) {
gameLoopThread.setRunning(false);
}
}
the problem because you still call the run method in thread after you try to block the thread and so you need to stop it before to block it using join. i tested your code with new implementation and it work well with me , feed me back
Problem
The problem is that when I'm in the game, and click the Home button, the gameLoop thread (I guess) gets messed up and then it pops up "Unfortunately, 'app name' has stopped." I've made a very simple program where this problem occurs. All this program does is showing a number on the screen, and increasing it when you touch the screen. When I comment out view.onDraw(c) in GameLoopThread, everything seems to run nicely. 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) ``` Here's the code: MainActivity.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; } } } ``` GameView.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); } } ``` GameLoopThread.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) {} } } } ```