I'm working on a small Android game that handles canvas drawing on a working thread. After the game is over this thread should start the next activity, but somehow the UI thread seems to get blocked by starting a new function.
This is my games Main-Loop:
while(true)
// Perform Ticks until the player makes a mistake
c = _surfaceHolder.lockCanvas();
if ((toDraw = rh.performTick()) == null) {
lose();
break;
}
_surfaceHolder.unlockCanvasAndPost(draw(toDraw,c));
}
The function performTick() processes the game logic and returns null when the game ends. draw()does the rendering. Both Functions work perfect - the problem is lose(), which does nothing than starting a new Activity:
protected void lose() {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
Intent intent = new Intent(_context, MainMenu.class);
_context.startActivity(intent);
}
});
}
When the Game ends, the new Activiy (MainMenu) starts normally but does not react on any touch events. There is no LogCat output and no Exception occuring. Even worse, lose()works normal when its called BEFORE the main loop starts.
I've been working on this for days now. I tried to start the Activity directly from the working Thread (without using a Handler in lose()) and searched the Internet for hours - nothing helps. It seems like the UI Thread is somehow blocked or caught in an infinite loop, and i have no idea how to fix this.
Everything is debugged on a Nexus 4 device.
Btw: MainMenu.onCreate() works normal when started another way.
Please help,
MA3o
You don't unlock the canvas when the if statement is true. Break will get you out of the while, leaving the canvas locked.
while(true)
// Perform Ticks until the player makes a mistake
c = _surfaceHolder.lockCanvas();
if ((toDraw = rh.performTick()) == null) {
lose();
break;
}
// this line won't be executed whether the if statement above is true
_surfaceHolder.unlockCanvasAndPost(draw(toDraw,c));
}
You should unlock inside the if statement also (before or after lose()).
Instead if While(true) you could have While(myVar) where myVar is a boolean that is true while game should be running and set to false when you call lose() In your case you continued to draw although you did not need to.
Related
Under some conditions, when my app starts, it displays an AlertDialog. However, the alert never gets displayed. I discovered that if I add a delay, it works (i.e. gets displayed).
More specifically: on app startup, it executes the main activity onCreate() which under a certain condition starts a 2nd activity. In the 2nd activity, through a separate thread, it makes a check for some web server status. If the Android device doesn't have Internet connectivity, HttpURLConnection returns an error instantly and my enclosing function executes a callback to the 2nd activity. My code then uses post() to attempt to display an alert to the user (using post allows displaying the alert on the UI thread, which is required).
Apparently it tries to display the alert before any of the either activity's UI has been created. If I use postDelayed() in the 2nd activity, the problem still persists. However, if I use the following block of code in the main activity, the alert shows properly:
new Handler().postDelayed (new Runnable ()
{
#Override public void run()
{
Intent intent = new Intent (app, MyClass.class);
app.startActivityForResult (intent, requestCode);
}
}, 3000);
My solution is a hack that happens to work at the moment. I don't mind having a little delay on start-up for this particular situation but I don't want a delay that's longer than necessary or one that may sometimes fail.
What is the proper solution?
Ok, here's a workaround. First, I'll speculate that the problem is that the attempt to display the alert is happening before the looper for the UI thread has been started. Just a speculation.
To work around the problem I added a recursive post which gets called from onResume(), like this:
private boolean paused = true;
#Override public void onResume ()
{
super.onResume();
paused = false;
checkForAlert();
}
#Override public void onPause ()
{
super.onPause();
paused = true;
}
And here's the function that does the post:
private AlertInfo alertInfo = null;
private void checkForAlert()
{
if (alertInfo != null)
{
...code to build alert goes here...
alertInfo = null;
}
if (!paused)
contentView.postDelayed (new Runnable()
{
#Override public void run() { checkForAlert(); }
}, 200);
}
AlertInfo is a simple class where the thread needing the alert can put the relevant info, e.g. title, message.
So, how does this work? checkForAlert() gets called during onResume() and will continue to get called every 200ms until "paused" is false, which happens in onPause(). It's guaranteed to be recurring whenever the activity is displayed. The alert will get built and displayed if alertInfo is not null. In the secondary thread, I simply create an AlertInfo instance and then within 200ms the alert gets displayed. 200ms is short enough that most people won't notice the delay. It could be shorter but then battery use goes up.
Why did I start checkForAlert() in onResume instead of onCreate()? Simply because there's no need for it to run unless the activity is currently "on top". This also helps with battery life.
I'm developing a small game on Android and I have a question. I want my main game loop to be the thread that is called from the SurfaceView but every time I want to call a method of another class from this thread I get the "stopped unexpectedly" message.
To be more clear:
#Override
public void run() { //Game Loop
Canvas canvas = null;
while (mRun) {
canvas = mHolder.lockCanvas();
if (canvas != null) {
grub.updateGrubber(); //here I want to update state.
mPanel.doDraw(canvas);
mHolder.unlockCanvasAndPost(canvas);
}
}
}
the line grub.updateGrubber(); show the error that I mention before. I tried different methods from that class and all of them gives me the same error.
Any help will be appreciated.
If my guess is correct, you are trying to update the UI thread directly. You can't call UI methods from threads other than the main thread. Try:
Runnable action = new Runnable() {
#Override
public void run() {
grub.updateGrubber();
} };
this.post(action);
instead of your `grub.updateGrubber();` line.
I'm actually tested what you suggested and is not a bad idea, but the problem in my code was that I never really created a Grubber object (I miss the = new Grubber(); part).
Probably a mistake related with the enormous amount of code writing and testing that I've done in the past few weeks, sorry about that. Anyway I'm going to use as main game loop the thread that is created to update the canvas in the surfaceview according to what i've read here.
I can't understand the implementation of a while loop in android.
Whenever I implement a while loop inside the onCreate() bundle, (code shown below)
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
TextView=(TextView)findViewById(R.id.TextView);
while (testByte == 0)
updateAuto();
}
nothing boots up, and the program enters a "hanging" state after a while and I can't understand why. Testbyte is as follows:
byte testByte == 0;
and updateAuto() is supposed to update the code per 1 second and display inside the textView portion. I've been using setText inside updateAuto() as shown below and everything works fine, but once i implement the while loop all i see is a black screen and then an option to force close after a few seconds due to it "not responding".
TextView.setText(updateWords);
I've changed it to a button format (meaning i have to click on the button to update itself for now), but i want it to update itself instead of manually clicking it.
Am i implementing the while loop in a wrong way?
I've also tried calling the while loop in a seperate function but it still gives me the black screen of nothingness.
I've been reading something about a Handler service... what does it do? Can the Handler service update my TextView in a safer or memory efficient way?
Many thanks if anyone would give some pointers on what i should do on this.
Brace yourself. And try to follow closely, this will be invaluable as a dev.
While loops really should only be implemented in a separate Thread. A separate thread is like a second process running in your app. The reason why it force closed is because you ran the loop in the UI thread, making the UI unable to do anything except for going through that loop. You have to place that loop into the second Thread so the UI Thread can be free to run. When threading, you can't update the GUI unless you are in the UI Thread. Here is how it would be done in this case.
First, you create a Runnable, which will contain the code that loops in it's run method. In that Runnable, you will have to make a second Runnable that posts to the UI thread. For example:
TextView myTextView = (TextView) findViewById(R.id.myTextView); //grab your tv
Runnable myRunnable = new Runnable() {
#Override
public void run() {
while (testByte == 0) {
Thread.sleep(1000); // Waits for 1 second (1000 milliseconds)
String updateWords = updateAuto(); // make updateAuto() return a string
myTextView.post(new Runnable() {
#Override
public void run() {
myTextView.setText(updateWords);
});
}
}
};
Next just create your thread using the Runnable and start it.
Thread myThread = new Thread(myRunnable);
myThread.start();
You should now see your app looping with no force closes.
You can create a new Thread for a while loop.
This code will create a new thread to wait for a boolean value to change its state.
private volatile boolean isClickable = false;
new Thread() {
#Override
public void run() {
super.run();
while (!isClickable) {
// boolean is still false, thread is still running
}
// do your stuff here after the loop is finished
}
}.start();
I am now working on an android app in which I need to display a text after some processing is done.
I'm using a Thread to run a process in the back while my progress dialog is being displayed to the user. The Thread works properly and I've followed it step by step and, apparently, it also ends fine; however, the method in which I call it does not seem to come to an end (at least, during a normal cycle) because the text I am setting afterward does display immediately, I have to wait and do some other action (like in order for it to display
Below is the piece of code I'm having trouble with:
private OnClickListener saldoDisp = new OnClickListener(){
public void onClick(View v){
int x = s2.getSelectedItemPosition();
branchSel = arrSucsId[x];
mainProc();
saldoAdminTex.setText(strSaldo); //The late one
}
};
public void mainProc(){
chekP = new Thread (null,doProc,"Background");
chekP.start();
mProgress =ProgressDialog.show(SivetaAsaldo.this, "","Obteniendo saldo...",true, false);
}
private Runnable doProc = new Runnable(){
public void run(){
if(getSaldoAdmin(levelSel,branchSel))
{
mProgress.dismis();
Log.i(TAG,"Task completed properly");
}else
handler.post(tosti);
}
};
So I do get the "Task completed properly" but seems like it still waits for something else, any clues guys?
Thanks for taking a bit of your time to check it out =).
saldoAdminTex.setText(strSaldo); //The late one
is going to get called immediately. It doesn't wait until after the Thread started in mainProc ends. You also cannot dismiss the Progress Dialog in your runnable. You can only do UI related things on the main UI thread.
It would help you to read the article on Painless Threading on the Android Dev site.
About your ProgressDialog, please see this answer about how to use a AsyncTask with a ProgressDialog.
Looking at your code, this:
saldoAdminTex.setText(strSaldo);
would potentially be executed before your thread finishes as the thread will be running in parallel to that line.
An alternative way would be to do this:
public void mainProc(){
mProgress =ProgressDialog.show(SivetaAsaldo.this, "","Obteniendo saldo...",true,false);
handler.post(new Runable(){
public void run(){
if(getSaldoAdmin(levelSel,branchSel))
{
mProgress.dismis();
saldoAdminTex.setText(strSaldo);
Log.i(TAG,"Task completed properly");
}else
handler.post(tosti);
}
});
}
I am trying to prevent my application calling the same method twice in the event of a double-click, or if the user presses different buttons quickly, almost at the same time.
I have clickable Views, acting as buttons, that call the same method but passing different parameters. This is the call:
startTheSearch(context, getState(), what, where);
Inside this method I'm creating a new Thread, because it queries a web server for the result:
new Thread(new Runnable() {
public void run() {
progDiag = ProgressDialog.show(ctx, null, "Searching", true);
getServerXML(context, what, where, searchIsCustom, mOffset);
handler.sendEmptyMessage(0);
}
}).start();
The problem is that upon two quick clicks, the method is fired twice, two threads are created, and consequently two new activities are created. That makes my app crash.
When the methods are done, and we have the result from the server, we call the handler:
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
try {
Intent i = new Intent(Golf.this, Result.class);
Bundle b = new Bundle();
b.putString("what", mWhat);
b.putString("where", mWhere);
b.putInt("offset", mOffset);
b.putBoolean("searchIsCustom", searchIsCustom);
i.putExtras(b);
startActivityForResult(i, Activity.RESULT_OK);
progDiag.dismiss();
} catch (Exception e) {
Alerts.generalDialogAlert("Error", "settings", ctx);
}
}
};
I tried to have a global boolean variable called "blocked" initially set to false, creating a condition like:
if(!blocked){
blocked = true;
new Thread(new Runnable() {
public void run() {
But this only seems to work on slower phones like the G1, I tried on Nexus and before it set blocked = true, the second request has was granted. So is there any way I can block the method being called if it's already running, or if the thread has started so it wont create a new one?
In the book Hello Android the author Ed Burnette gives a very nice solution for this problem.
To leave your app snappy and responsive you should create a second thread to do the web request.
Instead of creating a new thread for every request you can use a SingleThreadExecutor. If you start a second thread you can check if you are waiting for the result of another thread and block until this thread is ready, or cancel the old thread and start a new one if you think that the user now wants to do some other thing.
You can find the source code from the book on this page. Look for the Translate folder inside the source. It is a nice example on how to react to user commands and how to prevent too many web requests because of a user touching a lot on the gui.
Instead of using the "blocked" Boolean, you might investigate using a mutex. They're designed for this kind of problem.
I tried on Nexus and before it set
blocked = true, the second request has
was granted
That is impossible. Android UIs are single-threaded. There is no way that two onClick() methods of OnClickListeners will be called at the same time.