Reputation: 10095
I am writing a small game for Android. The game is drawn on a SurfaceView using a Thread. Within the run() method of the Thread, I test to see if the game is over and if it is, I try to display the game over dialog however that gives me the forementioned error message. I know that this error occurs when a non-UI thread tries to mess with the UI. What I'd like to know is the best approach to displaying such a dialog. I've pasted the code below. Thanks for your help:
public class BouncingBallActivity extends Activity{
private static final int DIALOG_GAMEOVER_ID = 0;
private BouncingBallView bouncingBallView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bouncingBallView = new BouncingBallView(this);
bouncingBallView.resume();
setContentView(bouncingBallView);
}
protected Dialog onCreateDialog(int id)
{
switch (id) {
case DIALOG_GAMEOVER_ID:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Game Over.")
.setCancelable(false)
.setPositiveButton("Try Again",
new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface arg0,
int arg1)
{
bouncingBallView.resume();
}
})
.setNegativeButton("Exit",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which)
{
BouncingBallActivity.this.finish();
}
});
AlertDialog gameOverDialog = builder.create();
return gameOverDialog;
default:
return null;
}
}
class BouncingBallView extends SurfaceView implements Runnable
{
SurfaceHolder surfaceViewHolder;
Canvas canvas;
Context context;
Thread drawingThread;
boolean drawingThreadIsRunning;
boolean isInitialised;
Ball ball;
ArtificialIntelligence ai;
BouncingBallView(Context context)
{
//
}
public void pause()
{
isInitialised = false;
drawingThreadIsRunning = false;
boolean joiningWasSuccessful = false;
while(!joiningWasSuccessful)
try {
drawingThread.join();
joiningWasSuccessful = true;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void resume()
{
isInitialised = false;
drawingThread = new Thread(this);
drawingThread.setName("Drawing Thread");
drawingThreadIsRunning = true;
drawingThread.start();
}
public void run()
{
while(drawingThreadIsRunning)
{
if(!surfaceViewHolder.getSurface().isValid())
continue;
if(gameOver())
BouncingBallActivity.this.showDialog(DIALOG_GAMEOVER_ID);
try{
canvas = surfaceViewHolder.lockCanvas();
if(!isInitialised)init(canvas);
update();
surfaceViewHolder.unlockCanvasAndPost(canvas);
}catch(Exception e)
{
Log.e(BouncingBallActivity.this.toString(),String.format("%s: Just as the emperor had foreseen!\n(This error is expected. Canvas destroyed while animations continue.)", e.toString()));
}
}
}
private void init(Canvas canvas)
{
ball = new Ball(canvas, Color.GREEN);
ai = new ArtificialIntelligence(canvas, (int) (ball.getX()+100),canvas.getWidth());
isInitialised = true;
}
}
}
Upvotes: 0
Views: 1644
Reputation: 1
For me i have used this handler in my surfaceView to create the dialogBox.
Handler someHandler = new Handler(){
//this method will handle the calls from other threads.
public void handleMessage(Message msg) {
final Dialog dialog = new Dialog(Game.this);
dialog.setContentView(R.layout.question_dialog);
dialog.setTitle("GAME OVER");
Button restart=(Button)dialog.findViewById(R.id.btn Restart);
// Set On ClickListener
restart.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Toast.makeText(Game.this, "Restart Game", Toast.LENGTH_LONG).show();
dialog.dismiss();
}
}
});
dialog.show();
}
So, i write the looper.prepared() in the game thread to call this Handler. If the player power = 0, this dialogBox will appear.
Looper.prepare();
//create the message for the handler
Message status = someHandler.obtainMessage();
Bundle data = new Bundle();
String msgContent = null;
data.putString("SOMETHING", msgContent);
status.setData(data);
someHandler.sendMessage(status);
Looper.loop();
}
Upvotes: 0
Reputation: 1
I used [https://stackoverflow.com/a/16886486/3077964] to solve the issue.
You need to pause the BouncingBallView's thread after showing the dialog inside the runOnUiThread(). That is:
//if(gameOver()){
BouncingBallActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() { BouncingBallActivity.this.showDialog(DIALOG_GAMEOVER_ID); } }); pause(); }
Upvotes: 0
Reputation: 8079
Try like this... you cannot make any changes to ui from other than main thread..put this part inside your thread after if(gameOver())
//if(gameOver())
runOnUiThread(new Runnable() {
@Override
public void run() {
BouncingBallActivity.this.showDialog(DIALOG_GAMEOVER_ID);
}
});
Upvotes: 2
Reputation: 2843
You are calling the Dialog from the worker(background) thread. You need to call this from the main thread. Try calling it using Activity.runOnUIThread() and create a handler inside that, which will call your showDialog method.
Upvotes: 1