2

I have written a game (I'll give it the name "Gamer") in which I have the following general structure (the code has been much simplified):

public class Gamer extends Activity implements View.OnClickListener
{
    public void onCreate(Bundle savedInstanceState) 
    {
        mpt = new MicksPanelThing(this);
    }
}
public class MicksPanelThing extends SurfaceView implements SurfaceHolder.Callback
{
    public MicksPanelThing(Context context) 
    {
        super(context);
        micks_thread_thing = new MicksThreadThing(getHolder(), this);
        getHolder().addCallback(this);
        setFocusable(true);
    }
    void updateView()
    {
        final SurfaceHolder holder = getHolder();

        try 
        {
            Canvas canvas = holder.lockCanvas();
        if(canvas != null)
            {
            onDraw(canvas);
            holder.unlockCanvasAndPost(canvas);
        }
        } 
        catch (Exception e) 
        {
        e.printStackTrace();
        }
    }
    public void onDraw(Canvas canvas) 
    {
        // blah
    }

}
class MicksThreadThing extends Thread 
{
    public MicksThreadThing(SurfaceHolder surfaceHolder, MicksPanelThing mpt)
    {
        // blah
    }
    public void run() 
    {
        while (this_thread_is_currently_active) 
        {
            micks_panel_thing.updateView();
        }
    }
}

The game was all working fine and was very robust. Now I wanted to test for a certain condition and, if it was true, put up an AlertDialog. I presumed that I could put this code within my onDraw method. I then got a message "Can't create handler inside thread that has not called Looper.prepare()" - this is confusing me because I assumed that the onDraw method was being executed by the main thread for which I presumed I needn't set anything up.

I presume I could solve this by either moving my test and dialog to somewhere within the main thread (where?) or by adding some Looper.prepare() code somewhere.

Please can someone tell me which would be easier - and where should any necessary code go.

Houcine
  • 24,001
  • 13
  • 56
  • 83
Mick
  • 8,284
  • 22
  • 81
  • 173

5 Answers5

2

try to use the method : runOnUIThread() to force the app to run your code on the mainThread ( which is the UIThread ) , Example :

class MicksThreadThing extends Thread 
{
    public MicksThreadThing(SurfaceHolder surfaceHolder, MicksPanelThing mpt)
    {
        // blah
    }
    public void run() 
    {
         Gamer.this.runOnUiThread(new Runnable(){
         @Override
         public void run(){
            //update/create your views here
            while (this_thread_is_currently_active) 
            {
                micks_panel_thing.updateView();
            }
        }
    });
    }
}

Other solution is : instead of using a Thread ,you can use an AsyncTask

Houcine
  • 24,001
  • 13
  • 56
  • 83
  • you said that you want to display your dialog from your Thread , so your thread can't touch or modify the UI , the only thread that can do that is the UIThread , so to force your thread to modify the UI ( Creating dialogs , views, modifiying views ...Etc) you should override the method runOnUIThread() to force your thread to run that code in the UIThread , – Houcine Jan 26 '12 at 11:21
  • Do you mean have a run *within* my run? or simply wrap my one existing run within the runOnUiThread?... Perhaps you can show the modified version of MicksThreadThing to avoid any confusion. – Mick Jan 26 '12 at 11:44
  • Thanks for the edit, but when I modify the code, eclipse highlights loads of new errors elsewhere in the code - for example The type Gamer must implement the inherited abstract method View.OnClickListener.onClick(View) ... why is this? – Mick Jan 26 '12 at 12:18
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/7066/discussion-between-mick-and-houcine) – Mick Jan 26 '12 at 12:34
1

do it in this way:

class MicksThreadThing extends Thread 
{
    public MicksThreadThing(SurfaceHolder surfaceHolder, MicksPanelThing mpt)
    {
        // blah
    }
    public void run() 
    {
        while (this_thread_is_currently_active) 
        {
            //micks_panel_thing.updateView();

            Gamer.this.runOnUiThread(new Runnable(){
               @Override
               public void run(){
                  micks_panel_thing.updateView();
               }
            });
        }
    }
}
waqaslam
  • 67,549
  • 16
  • 165
  • 178
  • is that run within run a typo? – Mick Jan 26 '12 at 11:26
  • no... the run inside Gamer.this.runOnUiThread will execute on your UI thread – waqaslam Jan 26 '12 at 11:47
  • Hmmm... well the code compiles - and I see the game state drawn, but then it is frozen - not responding to any touch events... is there anything else you may have forgotten? – Mick Jan 26 '12 at 11:58
0

You call onDraw(canvas); from MicksThreadThing. Obviously, it is not the main thread of the application. The main thread is those, which onCreate() of your activity is called.

Vladimir Ivanov
  • 42,730
  • 18
  • 77
  • 103
0

You can't update the UI from a normal Thread. Use runOnUiThread() for updating UI.

Thommy
  • 5,070
  • 2
  • 28
  • 51
0

the main thread is the ui thread... so you can use Handler or the onPostExecute() or onPreexecute() in the AsyncTask.

a fair player
  • 11,530
  • 9
  • 46
  • 48