Using Custom Threads

In this tutorial we will look at using threads in a Java MIDlet. We will create a run() method that implements some simple background logic, create a thread for that logic, and control the thread in the life cycle methods of the MIDlet.

Custom threads are a necessity for any program that has background processing that runs independently of the user-triggered events, such as key presses. For example, if you are writing an action-oriented game, you generally want the objects in your game to remain active, even when the user isn’t pressing a key. If you have a game that is completely event driven, without any background animation or processing, you might be able to get away without using a custom thread. Card games are an example of such a type of game.

One thing you don’t want to do is call your background logic directly from the startApp() method. The startApp() method should not contain any long-running logic, since the application isn’t considered fully-started until that method completes.

If you’ve implemented threads in Java SE, then Java ME threads will seem very familiar. Just as in Java SE, threads are represented by the java.lang.Thread class. Typically, you’ll implement the Runnable interface, put your logic in the run() method of your class, and pass an instance of your class to the Thread constructor when creating your main thread. Creating and starting of the main thread is often handled in the startApp() method of the application’s MIDlet class.

Using the code created in the Adding a Canvas tutorial, lets modify our Canvas class to implement the Runnable interface.


public class MyCanvas extends Canvas implements Runnable {

In order to implement Runnable, we will also need to add a run() method. For the moment, we’ll leave this blank. Also, let’s add an instance variable that can be used to control the state of the thread.


    public int state; // Current state

    // Possible states 
    public static final int STATE_RUNNING = 0; 
    public static final int STATE_STOP = 1;

    public void run() { }

Now, let’s take a look at our MIDlet class. We will need to add some code to the life cycle methods in order to control the starting and stopping of our main thread. In startApp(), we’ll create a Thread for our runnable MyCanvas object. In addition, we’ll stop our thread from the pauseApp() and destroyApp() methods. Note that I changed the canvas variable to be an instance variable rather than a local variable, so that it can be referenced from outside the startApp() method.


public void startApp() {
    Display display = Display.getDisplay(this);
    canvas = new MyCanvas();
    display.setCurrent(canvas);
    
    // Start main thread 
    canvas.state = MyCanvas.STATE_RUNNING; 
    new Thread(canvas).start();
    
}

public void pauseApp() {

    // Stop main thread 
    if(canvas != null) 
        canvas.state = MyCanvas.STATE_STOP;
        
}

public void destroyApp(boolean unconditional) {

    // Stop main thread
    if(canvas != null) 
        canvas.state = MyCanvas.STATE_STOP;

}

private MyCanvas canvas;

Now we have an application with a custom thread, however we still need to make it do something. At this point, I
think it would be easier to show you a simple example of some background logic that can be implemented in the run() method of our MyCanvas
object. The following example will draw a filled circle on the canvas, and move it back and forth.


public class MyCanvas extends Canvas implements Runnable {

    // Position; initialize coordinates to center of canvas 
    private int x = getWidth() / 2 - CIRCLE_DIAMETER / 2; 
    private int y = getHeight() / 2 - CIRCLE_DIAMETER / 2;
    private int xVel = 4; // Velocity
    public int state; // Current state

    // Possible states 
    public static final int STATE_RUNNING = 0; 
    public static final int STATE_STOP = 1;

    // Desired tick interval (10 ticks per second) 
    public static final int MILLIS_PER_TICK = 1000 / 10;

    // Circle diameter 
    public static final int CIRCLE_DIAMETER = 20;

    public void paint(Graphics g) {

        // Clear background 
        g.setColor(0x000000); 
        g.fillRect(0, 0, getWidth(), getHeight());

        // Draw circle 
        g.setColor(0xFF0000); 
        g.fillArc(x, y, CIRCLE_DIAMETER, CIRCLE_DIAMETER, 0, 360);

    }

    public void run() {

        // Calculate when the next tick should start 
        long nextStartTime = System.currentTimeMillis() 
            + MILLIS_PER_TICK;

        // Loop until thread is requested to stop 
        while(state != STATE_STOP) {

            // Determine if circle has reached edge 
            if(x + CIRCLE_DIAMETER + xVel >= getWidth() 
                    || x + xVel < 0) {

                // Reverse velocity 
                xVel = -xVel;

            }

            // Move circle 
            x += xVel;

            // Repaint canvas 
            repaint();

            // Get current time 
            long endTime = System.currentTimeMillis();

            // Compare current time to time when next 
            // tick should start 
            int difference = (int)(nextStartTime - endTime);

            // Sleep for the difference 
            if( difference > 0 ) {

                try {

                    Thread.sleep(difference);

                } catch(InterruptedException e ) { }

            }

            // Calculate when the next tick should 
            // start, relative to start time of 
            // the previous tick 
            nextStartTime += MILLIS_PER_TICK;

        }

    }

}


Using Threads

Obviously, this is a lot to digest, which is why I’ve tried to make the comments as
detailed as possible. However, there is one point I would like to touch on quickly. Notice the code that controls the tick length. The purpose of this code
is to try to maintain a constant tick rate, regardless of how long the logic in each tick takes. The code for this is deliberately designed to calculate the
tick length based on when the previous frame should have started, rather than when it actually started. This approach neutralizes any
slight variations caused by code other than the main application logic, such as the timing code itself.

The complete source code for the project described in this tutorial can be downloaded here: Source Code

Bookmark this Post

Leave a Reply