I'm following this tutorial on game development, and I've ran across something that is a little bit puzzling to me.
So in the tutorial, a class that extends SurfaceView implementing Runnable is created for managing the main game content view. The surface view is set as the content view of the main activity as following:
public class MainActivity extends Activity {
private GameView gameView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
gameView = new GameView(this);
setContentView(gameView);
}
#Override
protected void onPause() {
super.onPause();
gameView.pause();
}
#Override
protected void onResume() {
super.onResume();
gameView.resume();
}
}
The SurfaceView class is defined as following:
public class GameView extends SurfaceView implements Runnable {
Thread gameThread = null;
private boolean playing;
public TDView(Context context) {
super(context);
// other game logic
}
public void pause() {
playing = false;
try {
gameThread.join();
} catch (InterruptedException e) {
}
}
public void resume() {
playing = true;
gameThread = new Thread(this);
gameThread.start();
}
#Override
public void run() {
while (playing) {
update();
draw();
control();
}
}
}
Don't get me wrong - the code works perfectly, but I am confused as to where does the gameThread variable get instantiated? There is no line of code anywhere in my project indicating it does, besides in the resume method. Is the resume method called on creation of the GameView instance, or is there something else I am missing?
Excuse me if this is a silly question, but I couldn't figure out even how to google this.
pause() won't be called if Activity is not resumed. So this approach is fine. Everytime pause() is called gameThread is not null.
Related
I am trying to get a simple Runnable to execute some code every few seconds, but although I can get it to execute, I cant get it to stop. The code below shows 2 calls startDbChecking() and stopDbChecking(), I have just placed them in the code block to show what I'm attempting - not how the code is set up.
public class MainActivity extends TabActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startDbChecking(); // will run this no problem
stopDbChecking(); // but will not stop
}
public void startDbChecking() {
handler.post(runnableCode);
}
public void stopDbChecking() {
handler.removeCallbacks(runnableCode);
}
private Runnable runnableCode = new Runnable() {
#Override
public void run() {
// Do something here on the main thread
System.out.println("OK");
handler.postDelayed(runnableCode, 2000);
}
};
}
Try this in your Activity: to stop the runnable
protected void onStop() {
super.onStop();
handler.removeCallbacks(runnableCode);
}
Is it possible to create a background thread using andriodannotations thats starts in one activity and finish in another activity.
here is what i thought migh have worked
ActivityA
public static LoadingDialog LoadingScreen = new LoadingDialog();
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
//.....
LoadingScreen.CreateDialog(context);
}
Background Class
public class LoadingDialog
{
private Dialog loader_dialog;
#Background
public void CreateDialog(Context mContext)
{
loader_dialog = new Dialog(mContext,android.R.style.Theme_Black_NoTitleBar_Fullscreen);
loader_dialog.setContentView(R.layout.loading_screen);
loader_dialog.show();
}
public void Remove()
{
loader_dialog.dismiss();
}
}
the dialog displays correctly but when I finish() activityA to start activityB the thread appears to be killed to and i get a black screen. Any help on this would be much appreciated.
I have a very simple test application I'm making, and to set the seekBar's position I'm using a runnable. Although I have very little experience with actually working with a runnable.
public class MySpotify extends Activity implements Runnable {
private SeekBar progress;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.spotify_app);
myProgress = (SeekBar) findViewById(R.id.myBar);
}
#Override
public void run() {
myProgress.setProgress(25);
}
}
If I move myProgress.setProgress(25); into the onCreate then it works. But I want it to be set off in the runnable. Any ideas?
You need to post() a Runnable to a Thread for it to execute. Try calling post(this); inside onCreate().
Try
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.spotify_app);
myProgress = (SeekBar) findViewById(R.id.myBar);
myProgress.post(new Runnable()
{
public void run()
{
myProgress.setProgress(25);
}
});
}
You need something to run the post() method on
You can start the run method by just calling run();
Be aware that it will execute on the main thread.
Also be aware that it will run only once since there is no loop.
if you want to update while doing something else you sjould create a new thread.
example:
public class MySpotify extends Activity{
private SeekBar myProgress; //I asume it is call "myProgress" instead of "progress"
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.spotify_app);
myProgress = (SeekBar) findViewById(R.id.myBar);
ThreadExample example = new ThreadExample();
example.start();
/* Start a new thread that executes the code in the thread by creating a new thread.
* If ou call example.run() it will execute on the mainthread so don't do that.
*/
}
private class ThreadExample extends Thread{
public void run() {
myProgress.setProgress(25);
}
}
}
I'm making a simple Android game written in Java.
I have my activity...
public class GameName extends Activity{
...
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
Inside the "main" layout I have a button that calls a method called "startTheGame":
public void startTheGame(View v) {
theGame = new Panel(this);
setContentView(theGame);
}
Here is the panel code (simplified)
class Panel extends SurfaceView implements SurfaceHolder.Callback {
public GameThread _thread;
public Panel(Context context) {
super(context);
getHolder().addCallback(this);
setFocusable(true);
}
...
#Override
public void surfaceCreated(SurfaceHolder holder) {
_thread = new GameThread(getHolder(), this);
_thread.setRunning(true);
_thread.start();
}
...
}
So as you can see I have a "GameThread" class that is started... here it is below:
class GameThread extends Thread {
private SurfaceHolder _surfaceHolder;
private Panel _panel;
private boolean _run = false;
public GameThread(SurfaceHolder surfaceHolder, Panel panel) {
_surfaceHolder = surfaceHolder;
_panel = panel;
}
public void setRunning(boolean run) {
_run = run;
}
#Override
public void run() {
Canvas c;
while (_run) {
c = null;
try {
...
if (health <= 0) {
_run = false;
//change views?
setContentView(R.layout.over);
}
c = _surfaceHolder.lockCanvas(null);
synchronized (_surfaceHolder) {
_panel.onDraw(c);
}
...
} finally {
// do this in a finally so that if an exception is
// thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
_surfaceHolder.unlockCanvasAndPost(c);
}
try {
Thread.sleep(60);
} catch (Exception e) {
}
}
}
}
Hopefully you can see that if the health is <= 0 it stops the thread and I am trying to change to another layout I created called "over".
It just stops the screen from updating (drawing) but I never see the new panel.
I've tried:
GameName cs = ((GameName)getApplicationContext())
cs.setContentView(R.layout.over);
But I'm getting a ClassCastException...
Please Help!
You cannot cast your ApplicationContext to your main Activity (GameName) because this is not the same object. In a certain way, getApplicationContext() does not correspond to the first lauched Activity but to the app itself.
To me you should try to have distinct activities instead of trying to change the layout and the behaviour of a single Activity. It would be more simple for you and it would avoid the kind of issues you are facing.
Actually, each setContentView() I see in the code should correspond to a switch.
This way, you would have something like:
WelcomeActivity (probably GameName here): select the options of the game and start
PlayingActivity: the game itself. This is where the actual gameplay is.
GameOverActivity: displays the score, or anything you would like to show once the game ended
This without knowing which kind of game you are doing, but this skeleton should work well with arcade/action, roleplay/adventure or even maze/god-games.
I'd recommend looking into using Handlers. It allows you to safely communicate with the UI thread without worrying which thread you're currently on.
I have an android app I am just experimenting things on and I cannot seem to figure out why my app force closes when I update a TextView via a while loop. When I comment out the updateText method it runs fine.
public class GameThread extends Thread {
Thread t;
private int i;
private boolean running;
private long sleepTime;
GameView gv;
public GameThread() {
t = new Thread(this);
t.start();
i = 0;
sleepTime = 1000;
}
public void initView(GameView v) {
this.gv = v;
}
public void setRunning(boolean b) {
this.running = b;
}
public boolean getRunning() {
return running;
}
public void run() {
while(running) {
i++;
update();
try {
t.sleep(sleepTime);
} catch(InterruptedException e) {
}
}
}
public void update() {
gv.setText(i); // when this is uncommented, it causes force close
Log.v("Semajhan", "i = " + i);
}
public class GameView extends LinearLayout {
public TextView tv;
public GameView(Context c) {
super(c);
this.setBackgroundColor(Color.WHITE);
tv = new TextView(c);
tv.setTextColor(Color.BLACK);
tv.setTextSize(20);
this.addView(tv);
}
public void setText(int i) {
tv.setText("i count: " + i);
}
public class Exp extends Activity {
GameThread t;
GameView v;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
v = new GameView(this);
setContentView(v);
t = new GameThread();
t.setRunning(true);
t.initView(v);
}
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (t.getRunning() == true) {
t.setRunning(false);
Log.v("Semajhan", "STOPPED");
} else {
t.setRunning(true);
Log.v("Semajhan", "RESTART");
}
}
return true;
}
protected void onDestroy() {
Log.v("Semajhan", "DESTROYING");
super.onDestroy();
}
protected void onStop() {
Log.v("Semajhan", "Stopping");
super.onStop();
}
I though i'd post the whole app since it is relatively small and so that I could get some help without confusion.
First, when you get a Force Close dialog, use adb logcat, DDMS, or the DDMS perspective in Eclipse to examine LogCat and look at the stack trace associated with your crash.
In this case, your exception will be something to the effect of "Cannot modify the user interface from a non-UI thread". You are attempting to call setText() from a background thread, which is not supported.
Using a GameThread makes sense if you are using 2D/3D graphics. It is not an appropriate pattern for widget-based applications. There are many, many, many, many examples that demonstrate how to create widget-based applications without the use of a GameThread.
You have to call it from the UI thread.
For more info check: Painless Threading .
If you decide to use a Handler, the easiest solution for you will be to:
Extend a View, override it's onDraw , in it draw the game objects, after you have calculated the game data for them first of course
The Handler: (in your Activity)
private Handler playHandler = new Handler() {
public void handleMessage(Message msg) {
gameView.postInvalidate(); // gameView is the View that you extended
}
};
The game thread has a simple
Message.obtain(playHandler).sendToTarget();
In 2 words, the View is responsible for the drawing (you can move the calculations in a separate class, and call it before the onDraw), the thread is responsible only for scheduled calls to the Handler, and the Handler is responsible only to tell the View to redraw itself.
You cannot update the UI of your app outside of the UI Thread, which is the 'main' thread you start in. In onCreate(Context) of you app, you are creating the game thread object, which is what is doing the updating of your UI.
You should use a Handler:
http://developer.android.com/reference/android/os/Handler.html