Different threads for different purposes

You want your android application to be responsive, so you have to “multithread” it. But actually, “twothreading” should be enough in many cases.
The main thread, the one on which the application started, can be used to wait for user input like touchscreen events. So whenever the user does something, the event is caught on the main thread. The second thread’s job is just to draw on the screen. So, one thread for catching events and one for drawing, that’s it!
However, as they are separated thread, accessing their common data needs some synchronization. Obviously, when the user clicks something on the screen (once again, event caught by the main thread), the drawing thread needs to modify the drawing in consequence.
So, how did I do it concretely?
I created a drawing class that implements the interface Runnable. As you know, implementing Runnable is a much better approach than extending Thread. This class is responsible for all the drawing tasks in the game, responsible does not mean that it draws everything itself.
To be able to stop the thread, I use a volatile boolean run, which has to be set to truebefore the thread is started. The boolean is declared volatile to ensure that any thread that reads it sees its most recent value (I’m not sure it’s really needed here). Once runis false, the thread will be stopped for ever.
Here some pieces of my drawing class with the runmethod that implements Runnable:


public class SilhouDrawing implements Runnable {
...
  private final SurfaceHolder surfaceHolder;
  private final Handler handler;
  private final Context context;
...
  private volatile boolean run;
...
   public SilhouDrawing(SurfaceHolder surfaceHol, Context cont, Handler hand) {
    surfaceHolder = surfaceHol;
    handler = hand;
    context = cont;
  }
...
  public void run() {
    while (run) {
      Canvas c = null;
      try {
        c = surfaceHolder.lockCanvas(null);
        synchronized (surfaceHolder) {
          if (state == STATE_RUNNING) {
            doDraw(c);
          }
        }
      } finally {
        if (c != null) {
          surfaceHolder.unlockCanvasAndPost(c);
        }
      }
    }
  }
...
  public void setRunning(boolean b) {
    run = b;
  }
...
}

As soon assetRunninghas been called with falsethe thread stops doing anything because of the while condition. It does not mean that the thread has yet been destroyed, only that nothing is happening anymore in the run method.
With the statemember, the class knows if it’s worth drawing or not. If the activity would be hidden, the state value would be STATE_PAUSED and thus no CPU would be wasted drawing something that would not appear on the screen.
As already said, the doDraw method controls the drawing.


private void doDraw(Canvas canvas) {
  long now = System.currentTimeMillis();
  if (now - lastTime < 20) return;
  lastTime = now;
...
  canvas.drawBitmap(backgroundImage, 0, 0, null);
...
  for (Card c : CardsFactory.cards) {
    c.draw(canvas);
  }
...
}

So the first three lines (using the timing) are only there to save resources and to make sure the drawing happens 50 times per seconds (1000 / 50 = 20). I could put this in the run method or use the try{...Thread.sleep(20)... call. I don’t know what is better.
If time is fine, I draw the background image and then every card (try the game you will understand) is responsible to draw itself on the canvas.
So now, how do I create and use this class. That happens in the SurfaceView of my game.


public class SilhouView extends SurfaceView implements SurfaceHolder.Callback {
...
  private SilhouDrawing painter;
  private Thread thread;
  public SilhouView(Context context, AttributeSet attrs) {
    super(context, attrs);
    SurfaceHolder holder = getHolder();
    holder.addCallback(this);
    painter = new SilhouDrawing(holder, context, new Handler());
    setFocusable(true); // make sure we get key events
  }
...

So the SurfaceViewcreates the painter and passes it a reference to its SurfaceHolder to enable the drawing.
When the SurfaceView is created it will call (because of the callback mechanism) the surfaceCreated method which in turn gives our painter the right to start its job:


public void surfaceCreated(SurfaceHolder holder) {
  thread = new Thread(painter);
  painter.setRunning(true);
  thread.start();
}

If setRunning(true)would be called after thread.start(), the thread would never do anything as the while loop in the run code would fail.
To finally get rid of the thread, I use the thread.join()which will wait for the thread to die as the Java Api Specification says. I do this in the surfaceDestroyed(...) of my SurfaceView.


public void surfaceDestroyed(SurfaceHolder holder) {
  boolean retry = true;
  painter.setRunning(false);
  while (retry) {
    try {
      thread.join();
      retry = false;
    } catch (InterruptedException e) {
    }
  }
}

Ok, the code is very similar to examples that you might have seen somewhere else. But it seems to work. I will explore different ways to do it and let you know later about my experiments.
I hope you enjoy coding Java-Android as much as I do!

Leave a Reply

 

 

 

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>