i'm having a problem with restarting my game after clicking home or return button on android.
I've found partial solution in thread:
Android crash when app is closed and reopened, but the game after restarting dosen't work properly.
It only shows images/sprites which also moves, but dosen't read touches.
//this is in game view
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) { // ce se spremeni zaslon
}
public void surfaceCreated(SurfaceHolder holder) {
createSprites();
start();
}
public void start() {
loop.setRunning(true);
if (!mGameIsRunning) {
loop.start();
mGameIsRunning = true;
} else {
loop.onResume();
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
while (retry) {
try {
loop.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
//and this is game thread
while (running) {
canvas = null;
try{
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
beginTime = System.currentTimeMillis();
framesSkipped = 0;
this.gameView.update();
this.gameView.render(canvas);
synchronized (mPauseLock) {
while (mPaused) {
try {
mPauseLock.wait();
} catch (InterruptedException e) {
}
}
}
timeDiff = System.currentTimeMillis() - beginTime;
sleepTime = (int)(FRAME_PERIOD - timeDiff);
if (sleepTime > 0){
try{
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
}
}
}
} finally {
if (canvas != null){
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
public void onPause() {
synchronized (mPauseLock) {
mPaused = true;
}
}
public void onResume() {
synchronized (mPauseLock) {
mPaused = false;
mPauseLock.notifyAll();
}
}
Can anyone tell me if there is any mistake or any other solution.
Thanks for help.
Or if anyone could tell me for a good way to stop game and restart it, please?
I fixed it so that it works when i hit home, but when i hit back and restart it, I get black screen.
// current gameview code
public void surfaceCreated(SurfaceHolder holder) {
if (loop==null)
loop = new GameLoop(getHolder(), this);
if(loop.getState() == Thread.State.TERMINATED)
{
loop = new GameLoop(getHolder(), this);
}
start();
}
public void start() {
if (!mGameIsRunning) {
mGameIsRunning = true;
loop.setRunning(true);
loop.start();
} else {
loop.onResume();
loop.start();
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
loop.onPause();
}
I have a feeling that the problem is in surfaceDestroy method, but I'm not sure since I don't know what is the difference between hitting home or back key.
Related
how should I deal with the game loop when the user switches from my Android game to something else and then back?
Stopping and recreating the thread
Pausing/continuing the thread using wait() and notify()
Option #1 would look like this:
public void run() {
while (isRunning) {
// do stuff
}
}
#Override
protected void onPause() {
boolean retry = true;
gameLoop.setRunning(false);
while (retry) {
try {
mainThread.join();
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
super.onPause();
}
#Override
protected void onResume() {
if (gameLoop.isRunning()) {
stop();
}
gameLoop.setRunning(true);
mainThread = new Thread(gameLoop);
mainThread.start();
super.onResume();
}
Option #2 would look like this:
public void run() {
while (!mFinished) {
// do stuff
synchronized (mPauseLock) {
while (mPaused) {
try {
mPauseLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
#Override
protected void onPause() {
synchronized (mPauseLock) {
mPaused = true;
}
super.onPause();
}
#Override
protected void onResume() {
synchronized (mPauseLock) {
mPaused = false;
mPauseLock.notifyAll();
}
super.onResume();
}
Which option is the better one? And also should I use onPause()/onResume() or onStop()/onStart() to do these thread operations?
Thank you!
im trying to develop a game app.I implement onResume and onPause methods. onPause method works fine however onResume method crushes.
#Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
resume();
}
public void resume()
{
addAnime();
myThread.setRunning(true);
myThread.start();
}
if im not call update in onResume method myApp works fine but if i pause game and resume it again emulator says app not responding.
if i call resume in onResume method it says canvas is null at first runnig
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
view.resume();
}
I dont understand why canvas is null at the begining of the app when i call resume in onResume.Someone explain me?
EDIT
puase():
public void pause()
{
boolean repaet=true;
while(repaet)
{
try {
myThread.join();
myThread.setRunning(false);
repaet=false;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
myThread=null;
}
EDIT2
myThread
public void run() {
long ticksPS = 1000 / FPS;
long startTime;
long sleepTime;
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) {}
}
EDIT3
here is my set runnig function
public void setRunning(boolean run) {
running = run;
}
Pause method:
public void pause()
{
boolean repeat = myThread != null;
while(repeat)
{
try {
myThread.setRunning(false);
myThread.join();
repeat=false;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
myThread=null;
}
myThread:
public class gameThread extends Thread {
private SurfaceView view;
private boolean isRunning;
private static final int FPS; // set FPS with your value
private void sleep(long time) throws InterruptedException
{
Thread.sleep(time);
}
public void setRunning(boolean running)
{
synchronized(this)
{
this.isRunning = running;
}
}
public boolean isRunning()
{
synchronized(this)
{
return this.isRunning;
}
}
public void run()
{
long ticksPS = 1000 / FPS;
long startTime;
long sleepTime;
while (isRunning()) {
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) {}
}
}
Resume method:
public void resume()
{
addAnime();
// you must create a new thread. (You cannot use this same thread if it end)
myThread = new gameThread(surfaceView);
myThread.setRunning(true);
myThread.start();
}
Hi you can find further information in the documentation.
SurfaceHolder.lockCanvas
Start editing the pixels in the surface. The returned Canvas can be used to draw into the surface's bitmap. A null is returned if the surface has not been created or otherwise cannot be edited. You will usually need to implement Callback.surfaceCreated to find out when the Surface is available for use.
The content of the Surface is never preserved between unlockCanvas() and lockCanvas(), for this reason, every pixel within the Surface area must be written. The only exception to this rule is when a dirty rectangle is specified, in which case, non-dirty pixels will be preserved.
If you call this repeatedly when the Surface is not ready (before Callback.surfaceCreated or after Callback.surfaceDestroyed), your calls will be throttled to a slow rate in order to avoid consuming CPU.
about the use of Threads or Runneables, you can check this.
https://guides.codepath.com/android/Repeating-Periodic-Tasks
http://developer.android.com/guide/components/processes-and-threads.html
I actually can pause the thread correctly but when I press the unpause button
it doesn't resume. What am I doing wrong ?
public void pause() {
mRun = false;
while (true) {
try {
_thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
_thread = null;
}
public void unpause() {
mRun = true;
_thread = new GameBoardThread(this);
_thread.start();
}
I am starting a thread when invoking a method to play an audio file.
The code runs ok the first time but when I call the play method again I need the thread to start as if it were being called the first time. I have tried to interrupt the thread and even stop it but nothing seems to work.
How can I properly restart the thread?
Here is some code to help explain.
Global variable
private Thread thread1;
Thread code:
thread1 = new Thread(new Runnable()
{
#Override
public void run()
{
try {
int i=0;
final TextView timeDuration = (TextView) findViewById(R.id.timeDisplay);
final SeekBar seekBar = (SeekBar)findViewById(R.id.seekBar1);
while (running)
{
info();
j = 0;
while(i>0 && running)
{
while(j<duration && running && (playStatus.getValue() == "TRANSITIONING" || playStatus.getValue() == "PLAYING"))
{
seekBar.setMax((int) duration);
seekBar.setProgress(0);
runOnUiThread(new Runnable()
{
public void run()
{
System.out.println("PLAYBACK STATUS: "+playStatus.getValue());
timeDuration.setText(""+(j+1));
seekBar.setProgress(j+1);
if(j==(duration-1))
{
setRunning(false);
}
}
});
Thread.sleep(1 * 1000);
j++;
if(seekBar.getProgress() == seekBar.getMax())
{
runOnUiThread(new Runnable()
{
public void run()
{
playButton.setVisibility(View.VISIBLE);
pauseButton.setVisibility(View.GONE);
timeDuration.setText(""+"0");
seekBar.setProgress(0);
flag = false;
System.out.println("J VALUE 1: = "+j);
duration = 0;
setRunning(false);
}
});
}
}
}
j = 0;
i++;
Thread.sleep(1 * 1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
play();
This code works fine and plays the track. It then resets the seekbar and awaits for the play method to be called again.
public void play()
{
try
{
thread1.start();
}
catch(Exception e)
{
return;
}
}
Here is the setRunning method recommended to me.
public void setRunning(boolean b)
{
this.running = b;
}
If anyone know of a solution to this problem I would really appreciate it.
Threads are not supposed to be stopped manually. You should use a boolean instead of true in your while loop, and put the boolean to false through a setter when you want to stop:
private boolean running;
#Override
public void run(){
running = true;
while(running) {...}
}
public void setRunning(boolean b){
this.running = b;
}
To restart your player you need to prepare again.
public void restartAudio() {
Log.e(TAG, "restartAudio");
if (mp.isPlaying()) {
mp.seekTo(0);
}else{
mp.stop();
try {
mp.prepare();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
When using extra boolean flag to controll thread's while loop don't forget to use volatile modifier on it:
private volatile boolean running;
or put appropriate synchronization.
Apart from that, I'd think about using Thread.isInterrupted() method instead of the additional 'running' flag:
http://docs.oracle.com/javase/6/docs/api/java/lang/Thread.html#isInterrupted()
here's why:
https://stackoverflow.com/a/3196058/1350225
I am working on an android game.I was the facing the same issue as here
I tried the wait and notify all solution as shown over there.But as soon the app resumes i get Null pointer exception.
Here is the log cat info.
INFO/System.out(8166): on resume
INFO/System.out(8166): !!!! IN THE GAME RESUME ---- >>>
INFO/System.out(8166): SURFACE CREATED
INFO/System.out(8166): thread on resume
INFO/System.out(8166): in thread game pause=true
WARN/dalvikvm(8166): threadid=7: thread exiting with uncaught exception (group=0x4001d800)
ERROR/AndroidRuntime(8166): FATAL EXCEPTION: Thread-11
ERROR/AndroidRuntime(8166): java.lang.NullPointerException
ERROR/AndroidRuntime(8166): at com.org.GummyBlast.GameView.onDraw(GameView.java:442)
ERROR/AndroidRuntime(8166): at com.org.GummyBlast.GameLoopThread.run(GameLoopThread.java:88)
The code inside onSurfaceCreated and onSurfaceDestroyed
private void init() {
gameLoopThread = new GameLoopThread(this);
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
System.out.println(" SURFACE DESTROYED");
nullImages();
boolean retry = true;
//gameLoopThread.setRunning(false);
if(!mGameIsRunning){
while (retry) {
try {
gameLoopThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
System.out.println(" SURFACE CREATED");
Log.d("surface create", "SURFACE CREATED");
/*gameLoopThread.setRunning(true);
gameLoopThread.start();*/
start(holder);
}
public void start(SurfaceHolder holder) {
if (!mGameIsRunning) {
gameLoopThread.setRunning(true);
gameLoopThread.start();
mGameIsRunning = true;
} else {
gameLoopThread.onResume();
}
}
Code in my GameThread class
public class GameLoopThread extends Thread {
static final long FPS = 10;
private GameView view;
public static boolean running = false;
public static boolean run = true;
public static boolean game_pause=true;
private static Object mPauseLock;
private boolean mFinished=false;
long ticksPS = 1000 / FPS;
long startTime;
long sleepTime;
SurfaceHolder holder;
public GameLoopThread(GameView view) {
mPauseLock = new Object();
this.view = view;
}
public GameLoopThread() {
mPauseLock = new Object();
}
public void setRunning(boolean run) {
running = run;
}
#Override
public void run() {
while (running) {
// Do stuff.
System.out.println("in thread game pause="+Boolean.toString(game_pause));
if (game_pause == true) {
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) {
System.out.println("Error in game loop thread "+ e.getMessage());
}
}
else{
synchronized (mPauseLock) {
while (game_pause==false) {
try {
mPauseLock.wait();
} catch (InterruptedException e) {
}
}
}
}
}
}
public void onPause() {
synchronized (mPauseLock) {
game_pause = false;
System.out.println("thread on pause");
}
}
/**
* Call this on resume.
*/
public void onResume() {
synchronized (mPauseLock) {
game_pause = true;
mPauseLock.notifyAll();
System.out.println("thread on resume");
}
}
}
Activity class onPause
#Override
public void onPause() {
super.onPause();
System.out.println(" --- IN GAME PAUSE ---");
gameLoopThread = new GameLoopThread();
gameLoopThread.onPause();
}
how to maintain the state of the game in onResume?
Thanks
Maybe you need to call onResume() on your superclass:
public void onResume() {
super.onResume();
}
Hope this helps you.