by Eric Giguere
In addition to supporting traditional Java[tm] applications, the Personal Basis Profile (PBP) also supports the Xlet application model. Derived from the Java TV APIs, Xlets are applications whose lifecycle is closely controlled and monitored by the system. They are very similar to the MIDlets defined by the Mobile Information Device Profile. Xlets are also supported by the Personal Profile, which is a superset of the PBP.
An Xlet application's entry point is always a class that has a public no-argument constrUCtor, and that implements the Xlet interface:
package javax.microedition.xlet;
public interface Xlet {
void destroyXlet( boolean unconditional )
throws XletStateChangeException;
void initXlet( XletContext context )
throws XletStateChangeException;
void pauseXlet();
void startXlet()
throws XletStateChangeException;
}
Along with the Xlet interface, the new javax.microedition.xlet package also defines an XletContext interface and two classes, XletStateChangeException and UnavailableContainerException. I'll discuss these shortly.
The timing of an Xlet's creation is up to the application management software on the device. When it's first instantiated, the Xlet's no-argument constructor is invoked. The constructor is often empty because no information on the Xlet's context is yet available. After instantiating the Xlet, the system invokes its initXlet() method, which does most of the the Xlet's initialization.
The single argument passed to initXlet) is an object that implements the XletContext interface. The Xlet customarily stores a reference to this object because the methods it eXPoses allow the Xlet to communicate with the system, in order to effect state changes and to Access its environment:
public class MyXlet implements Xlet {
XletContext context;
public void initXlet( XletContext context )
throws XletStateChangeException {
this.context = context;
// do initialization here
}
// other methods
}
If the Xlet can't initialize itself successfully, it must throw an XletStateChangeException to notify the system, which will destroy it and remove it from the list of active applications. The initXlet() method is where you'll usually create the application's initial user interface, using the subset of AWT (or a toolkit based on it) supported by the profile.
Once the Xlet has returned from the initXlet() method, the system invokes the startXlet() method, although not necessarily immediately. The startXlet method tells the Xlet that it's entering the active state and that it can display its user interface and open the system resources it needs. If the Xlet is not ready to enter the active state, it can refuse the transition by throwing an XletStateChangeException. The system will then return the Xlet to a paused state and attempt to reactivate it later.
Once the Xlet is in the active state, the system can pause it at any time. When the system needs to pause the Xlet, it invokes the Xlet's pauseXlet() method. The Xlet is then no longer active, so it should release as many system resources as it can, to make them available to other applications. On return from pauseXlet() the Xlet enters the paused state. The Xlet is not suspended, however, and any background threads it started continue to run.
The system terminates the Xlet by invoking its destroyXlet() method. If the boolean argument to destroyXlet() is true, the termination is unconditional: the Xlet is destroyed without any choice in the matter. If the argument is false, the Xlet can refuse termination by throwing an XletStateChangeException.
Usually, the system handles Xlet state transitions. As with MIDlets, however, Xlets have ways to request state transitions explicitly. They can invoke methods of the XletContext interface:
package javax.microedition.xlet;
import java.awt.Container;
public interface XletContext {
String ARGS = new String();
Container getContainer() throws
UnavailableContainerException;
Object getXletProperty( String key );
void notifyDestroyed();
void notifyPaused();
void resumeRequest();
}
An Xlet can pause or terminate itself at any point by calling the notifyPaused() or notifyDestroyed() method, respectively. The Xlet immediately transitions to the desired state. Note that the Xlet is the one initiating the state transition, and neither pauseXlet() nor destroyXlet() is called, so the Xlet should perform the appropriate cleanup before calling notifyPaused() or notifyDestroyed().
An Xlet in the paused state can request activation by calling the resumeRequest() method. Note that immediate activation is not guaranteed: the system controls activation and may deny the request. When it approves activation, it invokes the Xlet's startXlet() method.
The remaining methods of the XletContext interface are used to retrieve properties, typically during Xlet initialization.
The getContainer() method returns the Xlet's root container -- the instance of java.awt.Container that the Xlet uses as its primary AWT component. The root container is either an instance of java.awt.Frame or a container whose ultimate parent is such an instance.
The getXletProperty() method returns application-specific properties. The PBP specification does not define what these properties are or how they are associated with the application, except the special value XletContext.ARGS, which is used to oBTain the command-line arguments used to start the application.
In a traditional Java application you access the command-line arguments through the parameter passed to the main() method:
public static void main( String[] args ){
.....
}
In the Xlet model you get the arguments by calling getXletProperty() from initXlet():
public void initXlet( XletContext context ){
String[] args = (String[])
context.getXletProperty( XletContext.ARGS );
.....
}
I'll end this discussion of the Xlet model by providing a simple Xlet that displays a message, then waits for the user to press a key before terminating itself. You can use this as a template for your own Xlets.
import java.awt.*;
import java.awt.event.*;
import javax.microedition.xlet.*;
/**
* A basic Xlet that puts up a single component and
* waits for a keypress to terminate it. Use as
* the basis for your own Xlets.
*/
public class BasicXlet implements Xlet {
private XletContext context;
private Container rootContainer;
private Frame rootFrame;
private SimpleTextLabel label;
// Defer most initialization to the
// initXlet method
public BasicXlet(){
}
// Called by the system to notify you that the
// Xlet is about to be destroyed.
public void destroyXlet( boolean unconditional )
throws XletStateChangeException {
exit();
}
// Called by the Xlet to terminate itself.
public void exit(){
rootContainer.setVisible( false );
context.notifyDestroyed();
}
// Returns the XletContext
public XletContext getContext(){
return context;
}
// Returns the root container
public Container getRootContainer(){
return rootContainer;
}
// Returns the root frame. The spec guarantees
// that the root container is either a frame or
// has a frame as an ancestor.
public Frame getRootFrame(){
return rootFrame;
}
// Obtains and stores the root UI components.
private void initUI() throws
XletStateChangeException {
// Store the root container and traverse
// the containment hierarchy to find the
// container's frame
try {
rootContainer = context.getContainer();
Container tmp = rootContainer;
while( !( tmp instanceof Frame ) ){
tmp = tmp.getParent();
}
rootFrame = (Frame) tmp;
}
catch( UnavailableContainerException e ){
// If we can't get the root container,
// abort the initialization
throw new XletStateChangeException(
"XletInitialization aborted --
no container: "
+ e.getMessage() );
}
catch( NullPointerException e ){
// This should never happen, but....
throw new XletStateChangeException(
"XletInitialization aborted --
no frame: "
+ e.getMessage() );
}
}
// Called by the system to initialize the Xlet.
public void initXlet( XletContext context )
throws XletStateChangeException {
this.context = context;
// store the context for later use
initUI();
// Now build our user interface
label = new SimpleTextLabel( "Press a key
to exit" );
label.setBackground( Color.blue );
label.setForeground( Color.yellow );
label.addKeyListener( new KeyAdapter(){
public void keyTyped( KeyEvent e ){
exit();
}
} );
rootContainer.add( label );
// Don't make the container visible here in
// case it's a frame, defer it until the
// startXlet method is called.
}
// Called by the system to pause the Xlet.
// This is where you release any system resources
// you are using. The system is likely to make
// the root frame invisible, so there's no reason
// to make the container invisible.
public void pauseXlet(){
}
// Called by the system to activate the Xlet.
public void startXlet() throws
XletStateChangeException {
// Make sure the container is visible
if( !rootContainer.isVisible() ){
rootContainer.setVisible( true );
}
}
}