ending android activity from surfaceview using thread.join - java

I'm working on my first app for android - snake. Right now I'm trying to implement 'game over' for that app. So basically when the snake hits the wall, app should ask your name and put it witch your score on HiScore. Well i'm stuk on hitting the wall fragment. Im experimenting with threads and so far I've found no way to stop it without getting errors.
I googled a lot, and everyone is saying that thread.join(); is waiting for thread to end, so how long does it take to end the thread that is drawing simple squares once per half second? When im hitting the back button on my phone while playing, pause(); function works perfectly. Log "game has ended" appears on LogCat.
So the problem is that i cant stop this activity when snake hits the wall, Log "game has ended" never occurs. Why is that?
My code:
public class SnakeCage extends SurfaceView implements Runnable{
// blah blah .. functions that draw and stuff ..
public void pause() {
isRunning = false;
while(true){
try {
aThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
aThread = null;
Log.d("pause()","game has ended"); // <<<<<<<<<THIS ONE>>>>>>>>>
}
public void resume() {
isRunning = true;
aThread = new Thread(this);
aThread.start();
}
public void init(){
// blah blah...
}
private void gameOver() {
int pHeadX = snakeHead.posX;
int pHeadY = snakeHead.posY;
Log.d("gameOver()", "checking");
if(pHeadY<0 || pHeadX<0 || pHeadX>23 || pHeadY>19){
Log.d("gameOver()", "game now will end");
gameOver = true;
}
}
public void run() {
while (isRunning){
if(!aHolder.getSurface().isValid())
continue;
canvas = aHolder.lockCanvas();
// drawing
gameOver();
if(gameOver) break;
// more drawing
aHolder.unlockCanvasAndPost(canvas);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(gameOver){
pause();
}
}
and the activity class:
public class PlayingActivity extends Activity implements OnClickListener{
SnakeCage v;
Button snakeGoUp;
Button snakeGoDown;
Button snakeGoLeft;
Button snakeGoRight;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.frame_layout);
v = (SnakeCage)findViewById(R.id.sView);
// listeners and stuff
}
#Override
protected void onPause() {
super.onPause();
v.pause();
}
#Override
protected void onResume() {
super.onResume();
v.resume();
}
#Override
public void onClick(View view) {
switch(view.getId()){
case R.id.snakeUp:
v.move(1);
break;
case R.id.snakeDown:
v.move(2);
break;
case R.id.snakeLeft:
v.move(3);
break;
case R.id.snakeRight:
v.move(4);
break;
}
}
}

i just did:
Context context = getContext();
((PlayingActivity)context).finish();
from:
How can I end an activity from inside a SurfaceView class or nested thread
although it does not satisfy me... its enough for now

I would suggest using a LocalBroadcastManager to send a message to the Activity, to notify it to kill itself. You can have an inner class in the Activity to receive the broadcast and call a private method in the Activity which will end it.
public class MyActivity extends Activity {
public void onCreate(Bundle state) {
LocalBroadcastManager.getInstance(this).registerReceiver(new MessageHandler(),
new IntentFilter("kill"));
}
private void killActivity() {
finish();
}
public class MessageHandler extends BroadcastReceiver {
onReceive(Context context, Intent intent) {
killActivity();
}
}
Then in your SurfaceView all you need to do is:
Intent intent = new Intent("kill");
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
Its a little more code but its a lot cleaner IMHO.

Related

calling a method after starting an activity

I'm making an Android whack a mole game. I have the main activity which is basically the launcher, when you press the Play button the game activity starts. This works fine as it shows the background image and all molehills but I don't know how to call the method to start the game.
I've tried to call it from inside onCreate() but this ends up "playing the game" itself.
I've tried to call it right after the startActivity(intent) but the app crashes. And also I've tried to create an instance of the game class and call the play() method after the start activity but it doesn't work aswell. I don't know how to start the game method once the game activity is loaded.
I hope I explained well, thank you.
public class MainActivity extends AppCompatActivity {
ImageButton btnStart;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Hide TitleBar
try { this.getSupportActionBar().hide();}
catch (NullPointerException e){}
setContentView(R.layout.activity_main);
btnStart = (ImageButton)findViewById(R.id.btnStart);
btnStart.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(), GameView.class);
startActivity(intent);
}
});
}
And this is the code for the game_activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Hide TitleBar
try { this.getSupportActionBar().hide();}
catch (NullPointerException e){}
setContentView(R.layout.activity_game_view);
game();
}
The game() method is a typical game loop.
public void game() {
Random random = new Random();
int index;
/*
* Casting array to store all ImageView on the game
*/
imgViewArray[0] = (ImageView)findViewById(R.id.img1);
imgViewArray[1] = (ImageView)findViewById(R.id.img2);
imgViewArray[2] = (ImageView)findViewById(R.id.img3);
imgViewArray[3] = (ImageView)findViewById(R.id.img4);
imgViewArray[4] = (ImageView)findViewById(R.id.img5);
imgViewArray[5] = (ImageView)findViewById(R.id.img6);
imgViewArray[6] = (ImageView)findViewById(R.id.img7);
imgViewArray[7] = (ImageView)findViewById(R.id.img8);
imgViewArray[8] = (ImageView)findViewById(R.id.img9);
imgViewArray[9] = (ImageView)findViewById(R.id.img10);
int j=0;
while (j < 10) {
// Get a random image to animate
index = random.nextInt(10);
switch(index) {
case 0: imgViewArray[0].setImageResource(images[6]);
new java.util.Timer().schedule(
new java.util.TimerTask() {
#Override
public void run() {
imgViewArray[0].setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
imgViewArray[0].setImageResource(images[0]);
}
});
}
},
300 // The code executes after 300ms
);
break;
I think you should put the game() call inside onResume().
There are many ways to solve the problem:
Using EventBus
Send the start game Event from Main Activity and register for the Event in the Game activity.
This is my favorite way to handle the problem. It's because the simplicity and prevent us from tightly coupled code. The major problem with using EventBus is we will lost in the sea of Event if there are too much Event in the the app.
How to do:
First, create the Event. This is just a simple class:
public class StartGameEvent {
}
Second, register for the event in the game activity:
public class GameActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
EventBus.getDefault().register(this);
}
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}
Third, subscribe for the event:
public class GameActivity extends Activity {
...
#Subscribe
public void onMessageEvent(StartGameEvent event) {
game();
}
}
Last, send the event from Main activity:
EventBus.getDefault().post(new StartGameEvent());
Using LocalBroadcastManager
You need to create the message and broadcast it in from your Main activity:
Intent intent = new Intent("playEvent");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
Then, in the game activity, you need to register as receiver:
#Override
public void onCreate(Bundle savedInstanceState) {
// register for the event
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver,
new IntentFilter("playEvent"));
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
game();
}
};
#Override
protected void onDestroy() {
// Unregister here
LocalBroadcastManager.getInstance(this)
.unregisterReceiver(mReceiver);
super.onDestroy();
}
I slightly modifying the code from How to use LocalBroadcastManager? for your case.
Using a static method in Game activity
This is the simplest way but highly discouraged. Because we can't ensure the state of the activity. Do not use this in production code. This is for learning sake only.
You can make the game() method as a static method like this:
public class GameActivity extends Activity {
...
public static void game() {
// game logic.
}
}
Then call the method when you want with:
GameActivity.game();

How to pause game when user turn off screen

I have problem with one of my games. This is time based puzzle game and I have problem with it. When user press on/off button on Android device game doesn`t stop, but timer goes on and on till game over. When user turn on screen again, he can see game over screen. But I want to make scenario when user press on/off button game will pause.
Any suggestions? I`m pretty new in programming so please explain me very basic method to do this.
Thanks all!
Edit. Timer code
private void initializeProgressBar() {
//initialize progressbar
progress = ApplicationConstants.GAME_TIME;
mProgress = (ProgressBarDetermininate) findViewById(R.id.progressDeterminate);
mProgress.setMax(progress);
mProgress.setProgress(progress );
timer = new Timer();
progressBarUpdateTask = new ProgressBarUpdateTask();
timer.schedule(progressBarUpdateTask, 20, 20);
}
class ProgressBarUpdateTask extends TimerTask {
#Override
public void run() {
runOnUiThread(new Runnable(){
#Override
public void run() {
progress-=1;
if(progress==0)
{
TimeOver();
}
mProgress.setProgress(progress);
}
});
}
#Override
protected void onResume() {
super.onResume();
}
#Override
protected void onPause() {
super.onPause();
this.timer.cancel();
}
Pause your game in onStop() or onPause() (depending on your need) in the Activity context your game is running in.
I am assuming you are using android's activity...
#Override
protected void onResume() {
super.onResume();
// Resume the timer or show a button for the user to press when ready
// !!! Also check if timer exits because onResume is called before onCreate!!!
}
#Override
protected void onPause() {
super.onPause();
// Pause the timer
}
When you press screen off or Power button onPause method of the application will be called and when you again press Power button, applications onResume method will be called, you should pause timer in onPause and resume it in onResume.
One way to do this is to detect the user presence, here's an example
At beginning of your game start the LockService
startService(new Intent(getApplicationContext(), LockService.class));
LockService.java
public class LockService extends Service {
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_USER_PRESENT);
final BroadcastReceiver mReceiver = new ScreenReceiver();
registerReceiver(mReceiver, filter);
return super.onStartCommand(intent, flags, startId);
}
public class LocalBinder extends Binder {
LockService getService() {
return LockService.this;
}
}
}
Then finally the BroadcastReceiver where you can stop your game.
ScreenReceiver.java
public class ScreenReceiver extends BroadcastReceiver {
public static boolean wasScreenOn = true;
#Override
public void onReceive(final Context context, final Intent intent) {
Log.e("LOB","onReceive");
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
// do whatever you need to do here
wasScreenOn = false;
/* PAUSE THE GAME HERE*/
Log.e("LOB","wasScreenOn"+wasScreenOn);
} else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
// and do whatever you need to do here
wasScreenOn = true;
}else if(intent.getAction().equals(Intent.ACTION_USER_PRESENT)){
}
}
}
As correctly mentioned in other answers, you can also use the onPause() method
#Override
protected void onPause() {
super.onPause();
// stop the game
}

Java Android on Ecplise - double gameover screen

I'm struggeling with following problem: I'm developing a small 2D Adroid game on Eclipse ( I'm a greenhorn). The basic idea is too keep a figure on the screen and when it leaves it , it's Game Over. The basic classes are the GameActivity, the GameView, the Figure and a GameOverActivity. Since my gaming-background is drawn on a simple RecF I check when the figure leaves the screen by
public class Figure{
public RectF getBounds(){
return new RectF(x, y, x + width, y+ height);
}
}
---------------------------------
public class GameView extends SurfaceView {
private GameActivity theGameActivity = new GameActivity();
private RectF figurebounds;
public void checkifout() {
figurebounds = figure.getBounds();
if (!(background.intersect(figurebounds))) {
theGameActivity.GameOver();
}
}
------------------------------------------
public class GameActivity extends Activity {
public void GameOver() {
Intent theNextIntent = new Intent(getApplicationContext(), GameOverActivity.class);
startActivity(theNextIntent);
this.finish();
}
}
The GameOverActivity is just a Layout with 2 buttons which allows to get back to the menu or replay
public class GameOverActivity extends Activity implements OnClickListener {
Button bReplay;
Button bBack;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.gameoverscreen);
initialize();
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.bReplay:
Intent newGameScreen= new Intent(this, GameActivity.class);
startActivity(newGameScreen);
this.finish();
break;
case R.id.bBack:
Intent newGameScreen2 = new Intent(this, MenuActivity.class);
startActivity(newGameScreen2);
this.finish();
break;
}
}
public void initialize(){
bReplay = (Button) findViewById(R.id.bReplay);
bReplay.setOnClickListener(this);
bBack = (Button) findViewById(R.id.bBack);
bBack.setOnClickListener(this);
}
At first I had the checkifout()-method within my onTouch-method (which is used to create another figures which have no influence on this issue) in the GameView-class , so I had to touch the screen after the figure fell off to get to the Gameover-screen, but at least it worked all fine:
public boolean onTouchEvent(MotionEvent event) {
if (System.currentTimeMillis() - lastClick > 300) {
lastClick = System.currentTimeMillis();
synchronized (getHolder()) {
xcor = event.getX();
ycor = event.getY();
checkifout();
}
}
return true;
}
But I'd like the GameOver-screen appear without another Touchevent.
Now when I call the checkifout()-method within my draw-method (also within the GameView class):
public void draw(Canvas canvas) {
drawBackGround(canvas);
figure.draw(canvas);
checkifout();
}
I get following problem: When my figure falls off the screen I do get the GameOver-screen, but after about a second it displays another time and when I go back to the main menu and try to exit the game I get back to the GameOverscreen, while replay stills works fine. I don't get those issues when I implement checkifout() in the OnTouchevent...
I thought it might be a problem of calling the checkifout()-method multiple times, so I tried to fix the issue by using a boolean like this :
private boolean gameover = false;
public void draw(Canvas canvas) {
drawBackGround(canvas);
figure.draw(canvas);
if (!gameover)
checkifout();
}
public void checkifout() {
figurebounds = figure.getBounds();
if (!(background.intersect(figurebounds))) {
theGameActivity.GameOver();
gameover=true;
}
But that gave me a NullPointerException.I also tried to create the checkifout-method within the Figure class and call it in the draw-method of the Figure-class, but that caused the same errors. So I hope someone has got an idea or just knows the mistake I've done here. Thanks for taking the time.
PS: Here is the MenuActivity (Layout with 2 buttons) in case it's needed:
public class MenuActivity extends Activity implements OnClickListener {
ImageButton bPlay;
ImageButton bExit;
#Override
public void onClick(View v) {
switch(v.getId()){
case R.id.bPlay:
Intent newGameScreen = new Intent(this, GameActivity.class);
startActivity(newGameScreen);
this.finish();
break;
case R.id.bExit:
this.finish();
break;
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mainmenu);
initialize();
}
public void initialize(){
bPlay = (ImageButton) findViewById(R.id.bPlay);
bPlay.setOnClickListener(this);
bExit = (ImageButton) findViewById(R.id.bExit);
bExit.setOnClickListener(this);
}
}
fixed, just seemed to be a temporal bug somewhere...

Start an Activity from Splash Screen, should I use run() or runOnUiThread()?

I have a Splash Screen (Logo Activity) to show the company name for 3 seconds before app starts. I start Main Activity from a thread, here is the code:
public class Logo extends Activity {
Thread t;
public boolean dead = false;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.logo);
t = new Thread() {
public void run() {
try {
Intent i = new Intent(Logo.this, Main.class);
Thread.sleep(3000);
if (!dead) {
startActivity(i);
}
finish();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t.start();
}
The Main Activity is called from a worked thread, is this correct? What are the differents with this code (using runOnUiThread)?
...
if (!dead) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Intent i = new Intent(Logo.this, Main.class);
startActivity(i);
}
});
}
...
I see no difference with this code in debug mode (The same threads, the same operation, etc.). Which is correct?
Starting an intent I think is not an UI operation. runOnUI thread runs UI operation on UI thread. So you can use either of thread (runOnUI or normal). May be normal thread will be good in this situation. But I would like to suggest you use timer instead.
To be honest, I don't like the Thread.sleep. PLease take a look at my solution:
new Timer().schedule(new TimerTask() {
#Override
public void run() {
// Do your work here like... startActivity...
}
}, SPLASH_DURATION); // SPLASH_DURATION IS IN MILLISECONDS LIKE 3000
Also you can block the user to prevent the back key like this:
#Override
public void onBackPressed() {
// do nothing! disable user interaction!
}
You should use AsyncTask in "doInBackground" background thread and than sleep your thread(this thread not UIThread) "PostExecute" run on UI Thread than start your new activity
private class mSplashViewer extends AsyncTask<Void,Void,Void>{
protected void doInBackground(Void params){
Thread.currentThread().sleep(3000);
return null;
}
protected void onPostExecute(){
startActivity(...);
}
}

Async Task with Media Player isn't Firing PublishProgress

I've written a AsyncTask:
public class AudioTransition extends AsyncTask <Void, Void, MediaPlayer>
{
private int goalID;
private int milliseconds;
private MediaPlayer tempPlayer;
AudioTransition(int ID, int Milliseconds)
{
goalID = ID;
milliseconds = (int)(((float)Milliseconds)/100);
}
#Override
protected void onPreExecute()
{
tempPlayer = MediaPlayer.create(context, goalID);
tempPlayer.setVolume(0, 0);
tempPlayer.setLooping(true);
tempPlayer.start();
}
#Override
protected MediaPlayer doInBackground(Void... arg0) {
for (int i = 0; i < 100; i++)
{
value = i;
publishProgress();
try {
Thread.sleep(milliseconds);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (!player.isPlaying())
tempPlayer.pause();
}
return tempPlayer;
}
#Override
protected void onProgressUpdate(Void... v) {
super.onProgressUpdate(v);
player.setVolume(100-value, 100-value);
tempPlayer.setVolume(value, value);
}
#Override
protected void onPostExecute( MediaPlayer result ) {
super.onPostExecute(result);
player.reset();
player = tempPlayer;
player.setVolume(100,100);
transitioning = false;
}
}
But the volume doesn't fade out. It just starts both tracks, then stops. The MediaPlayers are not updated until doInBackground completes. How can I make the MediaPlayers get updated within this type of background worker? It seems like the publishProgress() thing should work.
Oh lord. Dont be sleeping threads inside of AsyncTask! You are completely misusing AsyncTask. You couldn't think of another way to do a timer type thing, so you're latching onto the idea of publishprogress from AsyncTask (which doesn't even work how I think you think it works) even though AsyncTask should be used for one thing and one thing only: doing heavy work off of the main (UI) thread.
If you just wanted to fade the volume out then use something like this: (this goes somewhere outside of any method).
private Runnable VolumeFadeRunnable = new Runnable() {
#Override
public void run() {
volume--;
player.setVolume(volume, volume);
if(volume>0)
handler.postDelayed(this, 1000);
else
handler.removeCallbacks(this);
}
};
just initialize your handler as a field inside of onCreate or whatever and make sure that and the counter variable are visible inside of that runnable.

Categories