Hello Black Berry

In this section, you'll see the basics of how RIMlets work by creating a "Hello World'' RIMlet.

Note The term "RIMlet" is an informal name that you'll see in discussions on the Internet, but it doesn't appear in RIM's official documentation. As you'll see in the sections on compiling and building your application, RIM uses the term "CLDC application" for BlackBerry applications that aren't MIDlets. This choice of terminology is confusing because a MIDlet is also a type of CLDC application. Since RIM's proprietary APIs define the application life cycle, they act like an alternate profile, so the name "RIMlet" emphasizes the parallel and makes a lot more sense. I assume that the term "RIMlet" is a casualty of one of the usual hazards of making up names: alternate meanings (e.g., RIM looked up "RIMlet" in Urban Dictionary and decided it wasn't appropriate). Nonetheless, since the term "CLDC application" is ambiguous, in this book I'll use the term "RIMlet" for BlackBerry CLDC applications that are not MIDlets.

A RIMlet has the same two basic building blocks as a MIDlet, namely a main life cycle class (Listing 2-3) and a class that defines what appears on the screen (Listing 2-4). However, there are a couple of interesting differences, essentially due to the fact that a BlackBerry smartphone can run multiple applications simultaneously.

Running multiple applications concurrently isn't forbidden in MIDP, but since MIDP devices typically aren't capable of doing it, the precise behavior isn't specified prior to MIDP 3. On BlackBerry, you can specify that your application is to run in the background, and you can even specify that it is to be launched when the device is turned on and should run in the background the whole time. So the display screen component is not technically required. Since games typically don't run in the background, we're not going to spend much time on applications that don't use the screen.

The other big difference is that the BlackBerry platform has a screen stack built in, allowing the user to go to the previous screen just by pressing the back key. The RIM APIs are set up so that you can follow this same design pattern, pushing and popping your screens on the stack. This is one of the points where the RIM APIs improve on the design of MIDP. A user interface typically involves a stack of screens, but since MIDP doesn't have built-in support for stacking the screens, (outside of BlackBerry), your ad hoc implementation of a screen stack won't be well integrated with the device's standard navigation style.

Listing 2-3. HelloBlackBerry.java package net.frogparrot.hello;

import net.rim.device.api.ui.UiApplication;

* A simple Hello World example for BlackBerry.

public class HelloBlackBerry extends UiApplication {

* The RIMlet starts with main, just like an

public static void main(String[] args) { HelloBlackBerry helloBB = new HelloBlackBerry(); // Create a screen to write on and push it // to the top of the screen stack. helloBB.pushScreen(new HelloWorldScreen()); // Set the current thread to notify this application // of events such as user input. helloBB.enterEventDispatcher();

// don't put any code here - it won't be reached because // enterEventDispatcher() does not return.

After reading Listing 2-3, two questions should jump out at you: ''What's the event dispatcher?" and ''How does the application end?''

The event dispatcher is the thread that the platform uses to notify the application of user input events and update the screen. Even though a BlackBerry smartphone can run multiple applications at the same time, only one screen is at the top of the screen stack, and that's the screen that receives input events. Calling enterEventDispatcher() sets the event dispatcher thread to begin sending events to the screen that this application has pushed to the top of the stack.

If you have experience programming in Java, threading should be familiar to you. But if not, the metaphor of a ''thread'' (or string) works well. Just picture a thread passing through the code, making the different calls in the method. When one method calls another, the thread of command passes into the new method and returns when it's done. You can see that two threads can potentially be working their way through the same method simultaneously, for example.

Any sequence of commands that was launched by the platform calling a method, such as keyChar(), is running on the event thread (whereas methods called from within the run() method of a thread you started yourself won't be). If you're not sure, you can always call Application.isEventThread().

Note The enterEventDispatcher() method normally does not return because the thread that calls this method becomes the event-dispatching thread. Since the application ends with a call to System.exit()—which terminates the program completely—the enterEventDispatcher() method never returns to complete the method that called it. So any last-minute cleanup should be performed before calling System.exit().

The RIM user interface APIs follow the Java Swing philosophy of performing all user interface updates on the event thread instead of designing the user interface components to be thread safe. MIDP components work in essentially the same way—you may be able to access MIDP GUI components from multiple threads simultaneously, but you shouldn't. In either case, the platform uses the event thread when launching the application and when calling the methods that notify the application of user input. So, as long as you don't create any new threads yourself, all of your screen-update code (all of your code, in fact) will run on the event thread.

You can't just always use the event thread for everything, though—if a call on the event thread doesn't return quickly, the device can freeze up and crash. So you need to create a new thread when performing calculations that take a long time, for repeated events that are scheduled by a timer (as you'll see in Chapter 9), and for methods that block, such as communication code (as you'll see in Chapters 7 and 9). When using your own thread (not the event thread), the Java ME platform is designed to make it easy to delegate display updates to the event thread. You just call invalidate() on RIM components or repaint() on MIDP components to queue a request to update the screen, and the platform will call the paint() method from the event thread to carry it out.

If that's not sufficient, and you've created a method that needs to run on the event thread (to be synchronized with screen updates), then you can use Display.callSeriallyO (from a MIDlet) or Application.invokeLater() (from a RIMlet). You'll see how to use Application.invokeLater() in Chapter 5, as well as a more detailed example of how to use threads with Application.invokeAndWait() to update the game animation in Andrew's Swinging Saber game of Chapter 6.

The ''Hello BlackBerry'' application ends with a call to System.exit(), as shown in Listing 2-4. Typically you do this as soon as the last application screen has been popped off the stack. Since the Hello BlackBerry example program has only one screen, we have the program end itself as soon as the event dispatcher notifies the screen that it has been closed (i.e., popped off the screen stack). In more complex applications, I like to place the System.exit() call in a method in the UiApplication class, in order to keep the life cycle logic grouped, but as you can see from this example, it can be placed anywhere.

Actually, it's not even technically necessary to call System.exit() yourself since the platform's default behavior when closing the last screen on the screen stack is to call System.exit(). So, if the call to super() in Listing 2-4 were replaced by the call super(DEFAULT_CLOSE), then the onClose() method can be deleted from this class without affecting the behavior. But I think it's instructive to keep track of the application's life cycle explicitly so that there's no confusion about what it's doing behind the scenes.

Caution The BlackBerry platform doesn't launch your application in a separate virtual machine, which means that you have to be very careful about cleanup. The remains of an earlier run (such as static variables and other data still in memory) can potentially affect later runs of the application. It also means that there's a global namespace, so if two classes have the same name, errors can arise.

Of course, you still need to know how the screen got popped off the screen stack. The BlackBerry platform automatically places a Close option on the menu and handles its implementation (to close the screen and pop it off the screen stack). Listing 2-4 shows how you can add your own items to the menu as well, by overriding the screen's menu creation method, makeMenu(). Unlike the javax.microedition.lcdui.Command objects in Listing 2-1, net.rim.device.api.ui.MenuItem is an implementation of Runnable that handles its own command action in its run() method (which the platform calls when the user selects the MenuItem). That means that you have to implement a separate subclass for every MenuItem to specify its action. You can do this simply with an anonymous inner class, as you can see in the implementation of myToggleHelloItem in Listing 2-4.

Listing 2-4. HelloWorldScreen.java package net.frogparrot.hello;

import net.rim.device.api.ui.MenuItem; import net.rim.device.api.ui.component.BitmapField; import net.rim.device.api.ui.component.RichTextField; import net.rim.device.api.ui.component.Menu; import net.rim.device.api.ui.container.MainScreen;

import net.rim.device.api.i18n.ResourceBundle;

public class HelloWorldScreen extends MainScreen implements HelloBBResResource {

//---------------------------------------------------------

// instance fields

* This bundle contains all of the texts, translated by locale.

ResourceBundle myLabels = ResourceBundle.getBundle(BUNDLE_ID, BUNDLE_NAME);

* Whether or not the screen should currently display the

* "hello world" message.

boolean mySayHello = false;

* The user interface component to write hello on.

RichTextField myTextField;

* The "toggle hello" menu item.

MenuItem myToggleHelloItem

= new.MenuItem(this.myLabels.getString(HELLOBB_TOGGLE), 0, 0) { public void run() { toggleHello();

//-----------------------------------------------------

// initialization and state changes

* Write "Hello World!" on the screen.

HelloWorldScreen() { super();

myTextField = new RichTextField(myLabels.getString(HELLOBB_SAYHELLO),

RichTextField.NON_FOCUSABLE | RichTextField.READONLY); add(myTextField);

* Override the screen's menu creation method

* to add the custom commands.

protected void makeMenu(Menu menu, int instance) { menu.add(myToggleHelloItem); // separate the custom menu items from // the default menu items: menu.addSeparator(); super.makeMenu(menu, instance);

* Remove or replace the "Hello World!" message.

void toggleHello() { if(mySayHello) { myTextField.setText(myLabels.getString(HELLOBB_SAYHELLO)); mySayHello = false; } else { myTextField.setText(""); mySayHello = true;

* This is called by the BlackBerry platform when

* the screen is popped off the screen stack. */

public boolean onClose() { // end the program. System.exit(0);

// confirm that the screen is closing. return true;

The ResourceBundle class and the HelloBBResResource interface (which HelloWorldScreen implements) are part of RIM's built-in internationalization functionality. This is another point where RIM improves on MIDP. It makes sense to internationalize your application right from the beginning, but it's not built into MIDP. So, in MIDP you have to either reinvent the wheel—implementing your own way of mapping bundles of text data to the device language options—or use JSR 238, which was a bit of an afterthought and hence is not available on many platforms. For BlackBerry, the RIM Java platform defines a format for the resource files you include in your build, and it automatically generates a corresponding interface that your application can use to access the resources, as explained in the following section. Then the BlackBerry device transparently selects the right language resource bundle at runtime.

Was this article helpful?

+1 0

Post a comment