Sun Java Solaris Communities My SDN Account Join SDN
 
Wireless Tech Tips

J2ME Tech Tips: September 17, 2001

 

Tech Tips archive

September 17, 2001

WELCOME to the Java Developer Connection (JDC) Java 2 Platform, Micro Edition (J2ME) Tech Tips, for April 16, 2001. This issue covers:

The J2ME Tech Tips are written by Eric Giguere (http://www.ericgiguere.com), an engineer at iAnywhere Solutions, inc. Eric is the author of the book "Java 2 Micro Edition: Professional Developer's Guide" and co-author of the upcoming "Mobile Information Device Profile for Java 2 Micro Edition," both books in John Wiley & Sons' Professional Developer's Guide series.

Pixel

MAKING HTTP CONNECTIONS USING BACKGROUND THREADS

All user interface frameworks share some basic concepts. This is true no matter if the UI framework is the LCDUI classes defined by the Mobile Information Device Profile (MIDP), or the AWT and Swing classes defined by Java 2 Standard Edition (J2SE). One of the basic concepts is that the user interface must always be responsive to the user's interaction. In other words, if the user presses a button, moves a pointing device, or performs some other input operation, the user interface should act on that input as soon as possible. The usual rule of thumb is that the application should respond to input no later than a tenth of a second after it occurs. Longer delays are noticed by the user, and make the user interface seem unresponsive.

The key to user interface responsiveness, then, is to not do more than about a tenth of a second's worth of work in response to any user interface event. Why? When the application first starts, the user interface subsystem creates a thread to wait for and service input events. This thread, known as the event thread, invokes application-defined methods whenever events occur. In the MIDP, for example, the current screen's registered command listener is called when a command is triggered. Or if the current screen is a canvas, its keyPressed method is called whenever a key is pressed. The event thread cannot process further events until the invoked method (referred to as an event method) returns. If the event method performs a lengthy operation, the user interface is effectively blocked from processing further input, and sometimes even from repainting the display. The longer an event method takes, the more unresponsive the user interface seems. (Please note that this is a simplified description of user interface event handling -- the reality is a bit more complex. The description is accurate enough for our purposes.)

Responsiveness aside, there's another reason to avoid lengthy operations in response to events: you can't cancel an operation because the event thread is busy and is unable to process further user input.

So how do you ensure responsiveness and give users a way to cancel an operation? Simple: use worker threads to perform long operations, leaving the event thread free to handle user input and to repaint the display.

For a concrete example, consider an HTTP connection made using the MIDP's HttpConnection class. Simply connecting to the server can take several seconds from a wireless device, because at the present time wireless connections are very slow. Add to that the time for the server to respond and for the response to be received and processed by the application, and you can see that it's not unusual for an HTTP request and response cycle to take ten seconds or more. It makes sense, then, to display some kind of status message as the connection is made, and allow the user to cancel the connection entirely. Below is a simple class that does just that: it uses a separate thread to connect to a Web server and process the output. While the class does this, it displays the current status on a form, and allows the user to cancel the operation. You can use this simple application to see what HTTP headers are being sent back to the device when it connects to a specific Web server.

 import java.io.*;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
import javax.microedition.io.*;

// Simple class that connects to a given URL and
// prints out the response code and any headers
// that are sent back.  Shows how to use background
// threads to make HTTP connections while keeping the
// user interface alive.

public class HttpLogger extends MIDlet {

    private Display display;

    private Command exitCommand =
               new Command( "Exit", Command.EXIT, 1 );
    private Command okCommand =
               new Command( "OK", Command.OK, 1 );
    private Command cancelCommand =
               new Command( "Cancel", Command.CANCEL, 1 );

    private URLEntry mainForm;

    // Standard MIDlet stuff....

    public HttpLogger(){
    }

    protected void destroyApp( boolean unconditional )
                      throws MIDletStateChangeException {
        exitMIDlet();
    }

    protected void pauseApp(){
    }

    protected void startApp()
                      throws MIDletStateChangeException {
        if( display == null ){
            initMIDlet();
        }
    }

    private void initMIDlet(){
        display = Display.getDisplay( this );
        mainForm = new URLEntry();
        display.setCurrent( mainForm );
    }

    public void exitMIDlet(){
        notifyDestroyed();
    }

    // Utility routine to display exceptions.

    void displayError( Throwable e, Displayable next ){
        Alert a = new Alert( "Exception" );
        a.setString( e.toString() );
        a.setTimeout( Alert.FOREVER );
        display.setCurrent( a, next );
    }

    // Simple entry form for entering the URL to
    // connect to.

    class URLEntry extends TextBox
                   implements CommandListener {

        URLEntry(){
            super( "Enter URL:", "java.sun.com",
                   100, 0 );
            addCommand( exitCommand );
            addCommand( okCommand );
            setCommandListener( this );
        }

        public void commandAction( Command c,
                                   Displayable d ){
            if( c == exitCommand ){
                exitMIDlet();
            } else if( c == okCommand ){
                try {
                    HttpConnection conn =
                      (HttpConnection)
                      Connector.open( "http://" +
                                      getString() );
                    display.setCurrent(
                          new Logger( this, conn ) );
                }
                catch( IOException e ){
                    displayError( e, this );
                }
            }
        }
    }

    // Simple form that contains a single string item.
    // The string item is updated with status messages.
    // When constructed, it starts a background thread
    // that makes the actual HTTP connection.  Displays
    // a Cancel command to abort the current connection.

    class Logger extends Form
                 implements Runnable, CommandListener {
        private Displayable    next;
        private HttpConnection conn;
        private StringItem     text =
                     new StringItem( null, "" );

        Logger( Displayable next,
                HttpConnection conn ){
            super( "HTTP Log" );

            this.next = next;
            this.conn = conn;

            addCommand( cancelCommand );
            setCommandListener( this );

            append( text );

            Thread t = new Thread( this );
            t.start();
        }

        // Handle the commands.  First thing to do
        // is switch the display, in case closing/
        // the connection takes a while...

        public void commandAction( Command c,
                                   Displayable d ){
                    display.setCurrent( next );

            try {
                conn.close();
            }
            catch( IOException e ){
                displayError( e, next );
            }
        }

        // Do the connection and processing on
        // a separate thread...

        public void run(){
            update( "Connecting to " + conn.getURL() );

            try {
                int rc = conn.getResponseCode();
                update( "Response code: " + rc );
                update( "Response message: " +
                         conn.getResponseMessage() );

                String key;

                for( int i = 0;
                     ( key =
                       conn.getHeaderFieldKey( i ) )
                                             != null;
                     ++i ){
                    update( key + ": " +
                            conn.getHeaderField( i ) );
                }
            }
            catch( IOException e ){
                update( "Caught exception: " +
                        e.toString() );
            }

            removeCommand( cancelCommand );
            addCommand( okCommand );
        }

        // Update the string item with new information.
        // Only do it if we're actually visible.

        void update( String line ){
            if( display.getCurrent() != this ) return;

            String       old = text.getText();
            StringBuffer buf = new StringBuffer();

            buf.append( old );
            if( old.length() > 0 ){
                buf.append( '\n' );
            }

            buf.append( line );
            text.setText( buf.toString() );
        }
    }
}

Please note that like the original AWT classes, but unlike the Swing classes, the MIDP user interface classes are thread-safe. So invoking methods from different threads as done in this example is safe and legal.

Pixel

USING MIDP GAUGES

The Mobile Information Device Profile (MIDP) defines a number of high-level user interface components. Most of these components are designed to be placed on a top-level window called a form. Such components are referred to as items, because they all extend the javax.microedition.lcdui.Item class. The form itself is an instance of the javax.microedition.lcdui.Form class, and acts primarily as a container for items. The form is responsible for laying out the items and providing the means for the user to navigate from item to item, scrolling the items if necessary.

One of the items available to the MIDP programmer is the gauge component, javax.microedition.lcdui.Gauge. As you might surmise, a gauge is used to display a specific value within a range of possible values. Similar controls in other systems are often called progress bars; gauges are usually displayed using some kind of bar graph. The term "progress bar" is particularly appropriate because gauges are most often used to display the progress of lengthy operations.

The MIDP defines two types of gauge components: interactive and non-interactive. An interactive gauge lets the user modify the gauge value, ensuring that the value remains within the gauge's legal range. By comparison, a non-interactive gauge can only be modified programmatically -- there is no direct interaction with the user.

To use a gauge, you must first create a form on which to place the gauge:

    Form form = new Form( "My Form" );

The single argument to the constructor is the form's title. Keep the title short but descriptive. Note that the form won't be displayed until you call Display.setCurrent (as you'll see later in this tip).

Once you create the form instance, you can create a gauge instance. The Gauge class's constructor takes four arguments:

    public Gauge( String label, 
                  boolean interactive,
                  int maxValue,
                  int initialValue )

The first argument is the gauge's label, a short string that describes what the gauge represents. Although a label is recommended, it's not required, and null is an acceptable value for this argument. The second argument determines if the gauge is interactive or non-interactive. The third and fourth arguments specify the maximum and initial values of the gauge range. A gauge's minimum value is fixed at 0, so the maximum value must be a positive integer. The initial value must be greater than or equal to 0 and less than or equal to the maximum value.

Here is an example of a non-interactive gauge that displays values in the range 0 to 20, with an initial value of 10:

    Gauge gauge = new Gauge( "Read-only example", 
                             false, 20, 10 );

Making the gauge interactive is simply a matter of setting the second argument to true.

After you construct a gauge, add it to the form by calling the form's append method. This is one of several methods exposed by the Form class to manipulate the set of items displayed by a form. Here is a complete example:

    Display display = .... // obtain at startup
    Form form = new Form( "Gauge Example" );
    Gauge gauge = new Gauge( "Read-only example",
                             false, 20, 10 );
    form.append( gauge );
    display.setCurrent( form );

The application can change the gauge's current value at any time by calling the setValue method:

    gauge.setValue( 0 ); // reset the value

The current value can be read at any time by calling the getValue method:

    int curr = gauge.getValue();

Similarly, you can use the setMaxValue and getMaxValue methods to set and get the gauge's maximum value.

If you want an application to be notified when the user modifies an interactive gauge, the application must register a listener with the gauge's form. The listener implements the ItemStateListener interface:

    package javax.microedition.lcdui;
    
    public interface ItemStateListener {
        void itemStateChanged( Item item );
    }

The listener is registered by calling the form's setItemStateListener method. The listener's itemStateChanged method is invoked whenever the gauge's value changes.

Here's a simple example of a MIDlet that displays a form with a single, interactive gauge. The MIDlet traps the events from the gauge to update the gauge's title.

import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;

public class GaugeExample extends MIDlet {

    private Display display;

    private Command exitCommand =
               new Command( "Exit", Command.EXIT, 1 );

    // Standard MIDlet stuff....

    public GaugeExample(){
    }

    protected void destroyApp( boolean unconditional )
                      throws MIDletStateChangeException {
        exitMIDlet();
    }

    protected void pauseApp(){
    }

    protected void startApp()
                      throws MIDletStateChangeException {
        if( display == null ){
            initMIDlet();
        }
    }

    private void initMIDlet(){
        display = Display.getDisplay( this );
        display.setCurrent( new GaugeForm() );
    }

    public void exitMIDlet(){
        notifyDestroyed();
    }

    // Simple form containing a single gauge

    class GaugeForm extends Form
                   implements CommandListener,
                              ItemStateListener {

        private Gauge gauge;

        GaugeForm(){
            super( "Value: 0" );
            addCommand( exitCommand );
            setCommandListener( this );

            gauge = new Gauge( null, true, 25, 0 );
            append( gauge );
            setItemStateListener( this );
        }

        public void commandAction( Command c,
                                   Displayable d ){
            if( c == exitCommand ){
                exitMIDlet();
            }
        }

        public void itemStateChanged( Item item ){
            if( item == gauge ){
                setTitle( "Value: " +
                          gauge.getValue() );
            }
        }
    }
}


Pixel

- NOTE

Sun respects your online time and privacy. The Java Developer Connection mailing lists are used for internal Sun Microsystems purposes only. You have received this email because you elected to subscribe. To unsubscribe, go to the Subscriptions page (https://softwarereg.sun.com/registration/developer/en_US/subscriptions), uncheck the appropriate checkbox, and click the Update button.

As of May 22, 2001, Sun Microsystems updated its Privacy Policy (http://sun.com/privacy) to give you a better understanding of Sun's Privacy Policy and Practice. If you have any questions, contact privacy@sun.com.

- SUBSCRIBE

To subscribe to a JDC newsletter mailing list, go to the Subscriptions page (https://softwarereg.sun.com/registration/developer/en_US/subscriptions), choose the newsletters you want to subscribe to, and click Update.

- FEEDBACK

Comments? Send your feedback on the J2ME Tech Tips to:

jdc-webmaster@sun.com

- ARCHIVES

You'll find the J2ME Tech Tips archives at: http://java.sun.com/jdc/J2METechTips/index.html

- COPYRIGHT

Copyright 2001 Sun Microsystems, Inc. All rights reserved. 901 San Antonio Road, Palo Alto, California 94303 USA.

This document is protected by copyright. For more information, see:

http://java.sun.com/jdc/copyright.html

- LINKS TO NON-SUN SITES

The J2ME Tech Tips may provide, or third parties may provide, links to other Internet sites or resources. Because Sun has no control over such sites and resources, You acknowledge and agree that Sun is not responsible for the availability of such external sites or resources, and does not endorse and is not responsible or liable for any Content, advertising, products, or other materials on or available from such sites or resources. Sun will not be responsible or liable, directly or indirectly, for any damage or loss caused or alleged to be caused by or in connection with use of or reliance on any such Content, goods or services available on or through any such site or resource.

J2ME Tech Tips September 17, 2001

Sun, Sun Microsystems, Java, Java Developer Connection, J2ME, and J2SE are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries.