Now that we’ve seen what an Xlet looks like, let’s actually write one. In the grand tradition of first programs, we’ll start with a "Hello world" type of application.
This example should work on all JavaTV or MHP implementations. While all of the samples on this website have been tested on a real implementation, it’s important to remember that implementations do still differ, and until test suites are widely used, there may still be some changes needed for a particular platform.
Now that we’ve got the displaimers out of the way, let’s write the code. First, we’ll start with the basic skeleton. This class implements the javax.tv.xlet.Xlet interface
, and provides stub implementations of all its methods.
/** * The simplest Xlet that you will ever write. This Xlet * does absolutely nothing, not even print a message. * However, it is a complete skeleton Xlet that will * compile, even if it does nothing useful once it's been * compiled. * * This Xlet implements the Javax.tv.xlet.Xlet interface, * in order to be the main class for an Xlet. All methods * are inherited from javax.tv.xlet.Xlet */ public class MyFirstExampleXlet implements javax.tv.xlet.Xlet { /** * Every Xlet should have a default constructor that * takes no arguments. No other constructor will get * called. */ public MyFirstExampleXlet() { // The constructor should contain nothing. Any // initialisation should be done in the initXlet() // method, or in the startXlet method if it's time- // or resource-intensive. That way, the MHP // middleware can control when the initialisation // happens in a much more predictable way } /** * Initialise the Xlet. The context for this Xlet * will get passed in to this method, and a reference * to it should be stored in case it's needed later. * * This is the place where any initialisation should * be done, unless it takes a lot of time or resources. * If something goes wrong, then an * XletStateChangeException should get thrown to let * the runtime system know that the Xlet can't be * initialised. */ public void initXlet(javax.tv.xlet.XletContext context) throws javax.tv.xlet.XletStateChangeException { // Do nothing for now } /** * Start the Xlet. At this point the Xlet can display * itself on the screen and start interacting with the * user, or do any resource-intensive tasks. These * kinds of function should be kept in startXlet(), * and should *not* be done in initXlet(). * * As with initXlet(), if there is any problem this * method should throw an XletStateChangeException to * tell the runtime system that it can't start. * * One of the common pitfalls is that the startXlet() * method must return to its caller. This means that * the main functions of the Xlet should be done in * another thread. The startXlet() method should * really just create that thread and start it, then * return. */ public void startXlet() throws javax.tv.xlet.XletStateChangeException { // Do nothing for now } /** * Pause the Xlet. Unfortunately, it's not clear to * anyone (including the folks who wrote the MHP * specification) what this means. Generally, it means * that the Xlet should free any scarce resources that * it's using, stop any unnecessary threads, and remove * itself from the screen. * * Unlike the other methods, pauseXlet() can't throw an * exception to indicate a problem with changing state. * When the Xlet is told to pause itself, it must do so. */ public void pauseXlet() { // Do nothing for now } /** * Stop the Xlet. The boolean parameter tells the method * whether the Xlet has to obey this request. If the * value of the parameter is true, the Xlet must * terminate and the runtime system will assume that * when the method returns, the Xlet has terminated. If * the value of the parameter is false, the Xlet can * request that it not be killed, by throwing an * XletStateChangeException. If the MHP middleware * still wants to kill the Xlet, it should call * destroyXlet() again with the parameter set to true. */ public void destroyXlet(boolean unconditional) throws javax.tv.xlet.XletStateChangeException { // Do nothing for now } }
This is the most basic Xlet that you can get: it’s a valid Xlet that does absolutely nothing. You’ll notice that it has no constructor. This is deliberate. When the middleware starts an application, it first needs to create an instance of the main class. Doing this will invoke the default constructor (if it exists), and any code in the constructor will get executed. However, the Xlet has another method that should be used for this kind of initialization – the initXlet()
method. Doing this work in the initXlet()
method allows better control over when this happens and means that it only gets done when the Xlet is actually initialised. In short, do not provide a default constructor for your Xlet. Do all the initialisation work in the initXlet()
method, or in the startXlet()
method if the initialisation uses a lot of resources.
From our basic skeleton, let’s move forward and make it do something interesting. We will add some code to the four main methods – initXlet()
, startXlet()
, pauseXlet()
and destroyXlet()
. Since this is a simple Xlet, we don’t need to add any other classes or methods before we get our working Xlet. This example looks fairly scary, but it’s mostly comments:
// The main class of every Xlet must implement this // interface - if it doesn't do this, the middleware can't // run it. public class MySecondXlet implements javax.tv.xlet.Xlet { // Every Xlet has an Xlet context, just like the // Applet context that applets in a web page are // given. This is created by the MHP middleware and // passed in to the Xlet as a parameter to the // initXlet() method. private javax.tv.xlet.XletContext context; // A private field to hold the current state. This is // needed because the startXlet() method is called both // to start the Xlet for the first time and also to // make the Xlet resume from the paused state. This // field lets us keep track of whether we're starting // for the first time. private boolean hasBeenStarted; /** * Every Xlet should have a default constructor that * takes no arguments. No other constructor will get * called. */ public MySecondXlet() { // The constructor should contain nothing. Any // initialisation should be done in the initXlet() // method, or in the startXlet method if it's time- // or resource-intensive. That way, the middleware // can control when the initialisation happens in a // much more predictable way } /** * Initialise the Xlet. The context for this Xlet will * get passed in to this method, and a reference to it * should be stored in case it's needed later. This is * the place where any initialisation should be done, * unless it takes a lot of time or resources. If * something goes wrong, then an * XletStateChangeException should get thrown to let the * runtime system know that the Xlet can't be * initialised. */ public void initXlet(javax.tv.xlet.XletContext context) throws javax.tv.xlet.XletStateChangeException { // store a reference to the Xlet context that the Xlet // is executing in this.context = context; // The Xlet has not yet been started for the first // time, so set this variable to false. hasBeenStarted = false; // Since this is a simple Xlet, we'll just print a // message to the debug output System.out.println("The initXlet() method has been" + " called. Our Xlet context is " + context); } /** * Start the Xlet. At this point the Xlet can display * itself on the screen and start interacting with the * user, or do any resource-intensive tasks. These * kinds of function should be kept in startXlet(), * and should *not* be done in initXlet(). * * As with initXlet(), if there is any problem this * method should throw an XletStateChangeException to * tell the runtime system that it can't start. * * One of the common pitfalls is that the startXlet() * method must return to its caller. This means that * the main functions of the Xlet should be done in * another thread. The startXlet() method should * really just create that thread and start it, then * return. */ public void startXlet() throws javax.tv.xlet.XletStateChangeException { // Again, we print a message on the debug output to // tell the user that something is happening. In // this case, what we print depends on whether the // Xlet is starting for the first time, or whether // it's been paused and is resuming // have we been started? if (hasBeenStarted) { System.out.println("The startXlet() method has" + " been called to resume the Xlet after it's" + " been paused. Hello again, world!"); } else { System.out.println("The startXlet() method has" + " been called to start the Xlet for the" + " first time. Hello, world!"); // set the variable that tells us we have actually // been started hasBeenStarted = true; } } /** * Pause the Xlet. Unfortunately, it's not clear to * anyone (including the folks who wrote the JavaTV * specification) what this means. Generally, it means * that the Xlet should free any scarce resources that * it's using, stop any unnecessary threads and remove * itself from the screen. * * Unlike the other methods, pauseXlet() can't throw an * exception to indicate a problem with changing state. * When the Xlet is told to pause itself, it must do * so. */ public void pauseXlet() { // Since we have nothing to pause, we will tell the // user that we are pausing by printing a message on // the debug output. System.out.println("The pauseXlet() method has " + "been called. Bedtime..."); } /** * Stop the Xlet. The boolean parameter tells the * method whether the Xlet has to obey this request. * If the value of the parameter is true, the Xlet * must terminate and the runtime system will assume * that when the method returns, the Xlet has * terminated. If the value of the parameter is * false, the Xlet can request that it not be killed, * by throwing an XletStateChangeException. * * If the middleware still wants to kill the Xlet, it * should call destroyXlet() again with the parameter * set to true. */ public void destroyXlet(boolean unconditional) throws javax.tv.xlet.XletStateChangeException { if (unconditional) { // We have been ordered to terminate, so we obey // the order politely and release any scarce // resources that we are holding. System.out.println("The destroyXlet() method has" + " been called telling the Xlet to stop" + " unconditionally. Goodbye, cruel world!"); } else { // We have had a polite request to die, so we can // refuse this request if we want. System.out.println("The destroyXlet() method has" + " been called requesting that the application" + " stops, but giving it the choice. So, I'll" + " decide not to stop."); // Throwing an XletStateChangeException tells the // middleware that the application would like to // keep running if it's allowed to. throw new XletStateChangeException( "Please don't kill me!"); } } }
As you can see from this code, it simply prints out a different message when each method is called. Nothing complex, but enough to let you see what’s going on.
Now that we’ve got our code, we can compile it using javac
or using your favourite IDE (make sure that the JavaTV classes are in your classpath). We will use this example as the basis of some other Xlets. More code samples are available in the code library.