Have an activity with SurfaceView and a thread to paint on it (from this example). Here is GameSurface's finalization code:
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
while(retry) {
try {
this.gameThread.setRunning(false);
this.gameThread.join();
}catch(InterruptedException e) {
e.printStackTrace();
}
retry = true;
}
}
(don't really understand the logic of having retry always true, layed a bit with it but got no positive effect)
Here is the game thread:
package org.mineprogramming.laserminigolf;
import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;
import java.util.logging.Logger;
public class GameThread extends Thread {
private boolean running;
private GameSurface gameSurface;
private SurfaceHolder surfaceHolder;
public GameThread(GameSurface gameSurface, SurfaceHolder surfaceHolder) {
this.gameSurface = gameSurface;
this.surfaceHolder = surfaceHolder;
}
#Override
public void run() {
long startTime = System.nanoTime();
while(running) {
Canvas canvas= null;
try {
// Get Canvas from Holder and lock it.
canvas = this.surfaceHolder.lockCanvas();
// Synchronized
synchronized (canvas) {
this.gameSurface.update();
this.gameSurface.draw(canvas);
}
}catch(Exception e) {
// Do nothing.
} finally {
if(canvas!= null) {
// Unlock Canvas.
this.surfaceHolder.unlockCanvasAndPost(canvas);
}
}
long now = System.nanoTime() ;
// Interval to redraw game
// (Change nanoseconds to milliseconds)
long waitTime = (now - startTime)/1000000;
if(waitTime < 10) {
waitTime= 10; // Millisecond.
}
System.out.print(" Wait Time="+ waitTime);
try {
// Sleep.
this.sleep(waitTime);
} catch(InterruptedException ignored) { }
startTime = System.nanoTime();
System.out.print(".");
}
}
public void setRunning(boolean running) {
this.running= running;
}
}
Now I start some other activity:
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
And this activity is not responding (it is shown, however).
Tried also to start it for result.
The same thing when returning back via finish() from my activity with SurfaceView to another one.
After debugging for a little while, I see that initialization of the activity is successful, it freezes somewhere later
Ok, that was it. Retry was always true. This variant of finalization helped me:
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
while(retry) {
try {
this.gameThread.setRunning(false);
this.gameThread.join();
retry = false;
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
Not sure it is the best one, but it works for me.
Related
When using Surface View to make a game, I have a problem. I have a MainThread class, and all my update functions are in the run() function. The condition is:
while(running){
//do anything
}
I want to stop playing game and resume that by 2 menu items. In "stop item" I set thread.setRunning(false) and in "resume item" I set thread.setRunning(true), but it keeps running anyway!
After that, I tried to put a boolean inside the loop like this:
while(running){
if(isPlaying == true){
//do anything
}
}
And if I want to stop, I set the boolean variable to false and true to resume. But that doesn't work either. What do I need to do?
Try this:
while(running){
if(isPlaying == true){
//do anything
} else break;
}
If you set false, the thread will end and never stop again, unless you create a new thread. Therefore, you need to add a new menu item to control "quit". The "running" is used to control "resume" and "pause". For example:
public class MainThread extends Thread{
volatile boolean isQuit = false;
volatile boolean running = false;
Object lock = new Object();
public void setRunning(boolean running) {
this.running = running;
synchronized (lock) {
lock.notify();
}
}
public void setQuit(boolean isQuit) {
this.isQuit = isQuit;
}
#Override
public void run() {
// TODO Auto-generated method stub
super.run();
while (!isQuit) {
if (!running) {
System.out.println("pause ganme");
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println(e);
break;
}
}
}
else {
// continue game
System.out.println("running ganme");
}
}
System.out.println("quit ganme");
}
public void quit() {
isQuit = true;
running = false;
synchronized (lock) {
lock.notify();
}
}
}
I am new to Android. I'm trying to develop a game app using the surface view. The app runs properly but at the end when I exit it shows the null pointer exception on onDraw() method and the app gets crashed. Please help me out and thanks in advance.
Here is my code:-
public class Gameloopthread extends Thread {
private Gameview view;
static final long fps=30;
boolean running;
public Gameloopthread(Gameview view)
{
this.view=view;
}
// we have to tell thread to shut down & wait for it to finish, or else
// it might touch the Surface after we return and explode
public void setRunning(Boolean run)
{
running=true;
}
#Override
public void run() {
long tickps=3000/fps;
long startTime=0;
long sleepTime;
while(running)
{
Canvas c=null;
try{
c=view.getHolder().lockCanvas();
//Log.d("Canvas==>",""+c);
synchronized(view.getHolder()){
view.onDraw(c);
}
}finally{
if(c!=null){
view.getHolder().unlockCanvasAndPost(c);
}
}
sleepTime=tickps-(System.currentTimeMillis()-startTime);
try{
if(sleepTime>0)
{
sleep(sleepTime);
}else{
sleep(30);
}
}catch(Exception e){
}
}
super.run();
}
//
}
You should take a look at the lockCanvas call, the surface has probably been destroyed and you have passed a null canvas reference to view.onDraw(). There's some recommendations in the API on how to deal with surface state.
http://developer.android.com/reference/android/view/SurfaceHolder.html#lockCanvas()
I've searched over and over in this forum & google about this issue, maybe it has to do something with my thread or the way how the game is starting.
This is the skeleton of the game application, please tell me what I'm doing wrong.
This is what I'm doing in thread;
public class GameThread extends Thread
{
static final long FPS = 30;
private GameView view;
private boolean running = false;
private SurfaceHolder surfaceHolder;
public boolean isSurfaceCreated = false;
private Object mPauseLock;
private boolean mPaused;
public GameThread(SurfaceHolder surfaceHolder, Handler handler, GameView view)
{
super();
mPauseLock = new Object();
mPaused = false;
this.surfaceHolder = surfaceHolder;
this.view = view;
}
public void setRunning(boolean run)
{
running = run;
}
/**
* pause thread.
*/
public void onPause() {
synchronized (mPauseLock) {
mPaused = true;
}
}
/**
* resume thread.
*/
public void onResume() {
synchronized (mPauseLock) {
mPaused = false;
mPauseLock.notifyAll();
}
}
#Override
public void run()
{
// run our thread on high priority to keep from getting slowdown
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
// wait for surface to become available # start
while (!isSurfaceCreated && running)
{
try {
sleep(50);
} catch (InterruptedException e) {}
}
long ticksPS = 1000 / FPS;
long startTime;
long sleepTime;
while (running){
Canvas c = null;
startTime = System.currentTimeMillis();
//long msSinceLastTick = System.currentTimeMillis() - view.lastTickMs;
try {
c = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder)
{
/*
* stop updating when pause is pressed
*/
if(mPaused == false)
{
view.update();
}
view.onDraw(c);
}
} finally {
if (c != null)
{
surfaceHolder.unlockCanvasAndPost(c);
}
}
sleepTime = ticksPS - (System.currentTimeMillis() - startTime);
try {
if (sleepTime > 0)
sleep(sleepTime);
else
sleep(10);
} catch (Exception e) {}
}
}
This is the start activity code;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
public class test_game extends Activity {
/** Called when the activity is first created. */
private GameView Game;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Game = new GameView(this);
setContentView(Game);
}
#Override
public void onBackPressed()
{
super.onBackPressed();
//KILL ALL
finish();
System.runFinalizersOnExit(true);
System.exit(0);
}
// ===========================================================
// onPause
// ===========================================================
protected void onPause() {
super.onPause();
}
// ===========================================================
// onStop
// ===========================================================
protected void onStop() {
super.onStop();
}
// ===========================================================
// onDestroy
// ===========================================================
#Override
protected void onDestroy() {
super.onDestroy();
}
}
Added extra lines in AndroidManifest.xml
android:launchMode="singleTask"
android:alwaysRetainTaskState="true"
GameView looks like this:
public class GameView extends SurfaceView {
private SurfaceHolder holder;
private GameThread Thread = null;
private int testVar = 0;
private boolean isPaused = false;
public GameView(Context context)
{
super(context);
loadGameFiles();
holder = getHolder();
Thread = new GameThread(holder, new Handler(), this);
holder.addCallback(new SurfaceHolder.Callback()
{
#Override
public void surfaceDestroyed(SurfaceHolder holder)
{
boolean retry = true;
Thread.setRunning(false);
while (retry)
{
try {
Thread.join();
retry = false;
} catch (InterruptedException e)
{
}
}
}
#Override
public void surfaceCreated(SurfaceHolder holder)
{
Thread.isSurfaceCreated = true;
if(isPaused == true)
{
onResume();
}
Thread.setRunning(true);
Thread.start();
GameMenu();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
}
});
setFocusable(true);
}
public boolean isPaused()
{
return this.isPaused;
}
/**
* Pauses the physics update & animation.
*/
public void onPause() {
synchronized (holder)
{
if(isPaused == false)
{
isPaused = true;
Thread.onPause();
}
}
}
public void onResume()
{
synchronized (holder)
{
if(isPaused == true)
{
isPaused = false;
Thread.onResume();
}
}
}
etc..
Thanks for the replies, the forum commentbox doesn't let me post this; anyway, here it is:
Hi Frankenstein, I've tried what you suggested here, still doesn't work. My resume code in the GameView looks like this;
public void onResume(){ synchronized (holder)
{ if(isPaused == true){
isPaused = false; //resume notification to the gameview
Thread.onResume(); //resume the thread
}
}
}
This is on the Activity class;
#Override
protected void onPause() {
super.onPause();
Game.onPause();
}
#Override
protected void onResume() {
super.onResume();
Game.onResume();
}
I have not used logcat, not sure how to use it, I can sure call it in each steps in java but where does it displays the error/logs? thanks in advance guys!
When I resume it shows following error in LogCat;
Added this line to avoid the previous problem;
Thread.isSurfaceCreated = true;
Thread.setRunning(true);
Thread.start();
loadGame();
UPDATE:
The application returns to black screen, but it's not frozen.
Errors (updated) in logcat;
12-14 16:54:52.176: ERROR/MediaPlayerService(3937): create PVPlayer
12-14 16:55:03.172: ERROR/dalvikvm(4619): Failed to write stack traces to /data/anr/traces.txt (1093 of 3526): Unknown error: 0
12-14 16:55:03.910: ERROR/dalvikvm(25464): Failed to write stack traces to /data/anr/traces.txt (2401 of 3332): Math result not representable
12-14 16:55:03.918: ERROR/dalvikvm(25279): Failed to write stack traces to /data/anr/traces.txt (-1 of 2354): Math result not representable
12-14 16:55:32.394: ERROR/imdg81(19379): IsShutDownStarted()
12-14 16:55:32.590: ERROR/imdg81(19379): IsShutDownStarted()
12-14 16:55:37.902: ERROR/MediaPlayer(19379): error (100, 0)
12-14 16:55:37.930: ERROR/SoundPool(5264): Unable to load sample: (null)
12-14 16:55:39.281: ERROR/SoundBooster(5328): readSBTable: file open error!
12-14 16:55:39.281: ERROR/AcousticEQ(5328): [AEQ] aeqcoe.txt: file open error!
12-14 16:55:39.500: ERROR/AudioService(19379): Media server died.
12-14 16:55:39.500: ERROR/AudioService(19379): Media server started.
12-14 16:55:39.996: ERROR/MediaPlayerService(5328): create PVPlayer
In your onSurfaceCreated method you have this block of code:
if(isPaused == true){
onResume();
}
Thread.setRunning(true);
Thread.start();
your call to Thread.start() is called no matter what (even if isPaused is true). Since your thread is already started in this case, you're trying to start it twice and thus getting an illegal state exception.
Thanks guys!
I've fixed this issue, the black screen was showing because the thread isn't created again but being manipulated when I entered 2nd time to the screen. Therefore, it showed an empty/null thread being manipulated as black screen. I've added following line to OnResume()
if(!Thread.isAlive())
{
Thread = new GameThread(holder, new Handler(), this);
}
This is the fix for my issue, but now I've to implement onSaveInstanceState & onRestoreInstanceState to avoid the from re-starting from beginnig.
i have a thread problem with my surfaceView, its quite common but i have not found a solution anywhere.
I use a SurfaceView as a Background in a frame layout:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/blobmusic_layout"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.myapp.Background
android:id="#+id/background_blobmusic"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
...
and my background class is the tipical surfaceview class, with the holder, the thread wich will call our onDraw method, etc. Some code to avoid questions (init() is called in the constructor methods):
public void init() {
ready = false;
holder = getHolder();
holder.addCallback( new Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
boolean retry = true;
while (retry) {
try {
bgdt.setRunning(false);
bgdt.join();
retry = false;
} catch (InterruptedException e) {}
}
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
holder.setFormat(PixelFormat.RGBA_8888);
estrelles = new Estrelles(getResources(),getHeight());
bgdt.setRunning(true);
bgdt.start();
ready = true;
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
});
and my thread class (bgtd):
public class BGDrawThread extends Thread {
private Background view;
static final long FPS = 60;
private boolean running = false;
public BGDrawThread(Background view) {
this.view = view;
}
public boolean isRunning() {
return running;
}
public void setRunning(boolean run) {
running = run;
}
#Override
public void run() {
long ticksPS = 1000/FPS;
long startTime;
long sleepTime;
//fps checker
long contms=0;
long lasttimecheck = System.currentTimeMillis();
int fps=0;
while (running) {
if(contms>1000) {
//souts ??
contms=0;
fps=0;
lasttimecheck = System.currentTimeMillis();
}
else {
fps++;
contms+=System.currentTimeMillis()-lasttimecheck;
lasttimecheck=System.currentTimeMillis();
}
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) {}
}
}
}
This works fine, until another activity comes to front and then we come back from it, that crashes.
Here's my code of onpause/onresume methods of the class:
#Override
protected void onResume() {
super.onResume();
Background bmbg = (Background) findViewById(R.id.background_blobmusic);
bmbg.resumethread();
}
#Override
protected void onPause() {
super.onPause();
Background bmbg = (Background) findViewById(R.id.background_blobmusic);
bmbg.stopthread();
}
and the strop/resume thread in the surfaceview class:
public void stopthread() {
if(!bgdt.isAlive()) return;
boolean retry = true;
while (retry) {
try {
bgdt.setRunning(false);
bgdt.join();
retry = false;
} catch (InterruptedException e) {}
}
}
public void resumethread() {
if(ready && !bgdt.isRunning()) {
bgdt.setRunning(true);
bgdt.start();
}
}
All help is welcome :)
EDIT: i made it run (probably not an elegant solution, but i leave the code here):
(look at the comment below to see the other changes)
public void startthread() {
if(!surfacecreated) return;
if(threadrunning) return;
bgdt = new BGDrawThread(this);
bgdt.setRunning(true);
bgdt.start();
threadrunning = true;
}
public void stopthread() {
boolean retry = true;
while (retry) {
try {
bgdt.setRunning(false);
bgdt.join();
threadrunning = false;
retry = false;
} catch (InterruptedException e) {}
}
}
IIRC surface is ready for manipulations only after surfaceChanged() callback, not surfaceCreated() , and surfaceChanged() is called after onResume() - move your initialisation and thread startup there ( to surfaceChanged() )
I have a game I'm developing for Android 2.x. When the player looses, I want to automatically exit that and go back to the previous menu of the game. This may be a simple question, but how do I do that?
I have a MenuActivity, which calls GameActivity, which calls the GameView (implementation of SurfaceView) where the game logic is.
UPDATE: No, wait, it's more complex than that. To make this game I followed the MoonLander example, which used threads.
GameView:
public class GameActivity extends Activity
{
GameView gameView;
GameThread gThread;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
gameView = new GameView(this);
setContentView(gameView);
gThread = gameView.getThread();
gThread.doStart();
}
}
GameView:
package com.cp.balldrop;
public class GameView extends SurfaceView implements SurfaceHolder.Callback
{
class GameThread extends Thread
{
public GameThread(SurfaceHolder sHolder, Context context, Handler handler)
{
//Code
}
public void doStart()
{
synchronized (mSurfaceHolder)
{
}
}
#Override
public void run()
{
while (running)
{
Canvas c = null;
try
{
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder)
{
doDraw(c);
}
}
finally
{
if (c != null)
{
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
protected void doDraw(Canvas canvas)
{
//code
}
}
/** Handle to the application context, used to e.g. fetch Drawables. */
private Context mContext;
/** Pointer to the text view to display "Paused.." etc. */
private TextView mStatusText;
/** The thread that actually draws the animation */
private GameThread gThread;
public GameView(Context context)
{
super(context);
// register our interest in hearing about changes to our surface
SurfaceHolder holder = getHolder();
holder.addCallback(this);
// create thread only; it's started in surfaceCreated()
gThread = new GameThread(holder, context, new Handler()
{
#Override
public void handleMessage(Message m)
{
// mStatusText.setVisibility(m.getData().getInt("viz"));
// mStatusText.setText(m.getData().getString("text"));
}
});
setFocusable(true);
}
public GameThread getThread()
{
return gThread;
}
public void surfaceCreated(SurfaceHolder holder)
{
gThread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder)
{
boolean retry = true;
gThread.setRunning(false);
while (retry)
{
try
{
gThread.join();
retry = false;
}
catch (InterruptedException e)
{
}
}
}
}
In my thread, I call the following method from my surfaceview's doTouch:
public void doLose() {
synchronized (mSurfaceHolder) {
//quit to mainmenu
((Activity) mContext).finish();
}
}
This seems to shut down the surfaceview and the activity hosting it, dumping me back in my main-menu activity. Note the casting and the predefined context.
What about calling finish() on the GameActivity?