Wednesday, July 7, 2010

Blackberry UI: Events, Screens and Threads


Creative Commons Licence
This work is licenced under a Creative Commons Licence.

There are several things that you must always bear in mind when writting BlackBerry applications: the device is event driven on a single Java Virtual Machine (JVM); the user interface is implemented using screens which are held on a screen stack; and the OS is multi-threaded, some threads have special requirements and capabilities which must be respected.

Driven by Events


Event driven software is not a new concept, especially where graphical user interfaces (GUIs) are concerned. When the user interacts with part of the interface, that interaction is presented to the appropriate software as an event. As programmers our job becomes writing little code fragments that are all related but each one takes care of some element of the user interface. This is a very powerful concept and has allowed much of what we take for granted on our digital devices to be developed. It does mean that we are no longer able to say ahead of time which order our bits of code will be executed in. That power is given over to the user.

On the BlackBerry platform, events are delivered to Listeners. A Listener is simply an object that implements a particular interface, a method or group of methods that provide the object with a specified behaviour. We don't need to concern ourselves too much with the details of how the event delivery mechanism works, but we do need to keep certain things in mind. The operating system collects the very low level events: key presses and releases, track ball movement, data input/output etc. and delivers them to applications by placing the events in an event queue. When an application gets a turn to execute, a waiting event is taken off the queue and delivered to any listeners registered for that event. The listener may be provided by the OS or the API and it may combine many low level events into one or more higher level events. For example track ball movement and clicks may be combined into a drop down list item selection. These higher level events are then dilivered to their listeners in turn. All this event delivery is done by the Event Thread, and there is only one Event Thread. So if your program receives an event and starts a process that will take a long time (like computing Pi to thousands of decimal places) or that may block waiting for something external to the device (like downloading a web page) no other applications will receive events while your greedy program hogs the Event Thread. This will cause the device to appear to freeze. If this goes on too long, the event queue will fill up and the OS will terminate your program. So, keep your listeners short and quick. If you need to do long computation or IO start a new thread to handle those tasks.

The Event Thread, or more precicely the thread that is holding the user interface event lock is the only thread that can manipulate user interface elements. There are ways of arranging for your code to be executed on the Event Thread, or to grab the event lock, so that non-Event Threads can still have their results displayed to the user. We will look at those a little later.

Stack those Screens


Within a UiApplication Screens are held on a stack. The topmost screen is displayed to the user and receives events directed to the application. To display a Screen an Object that is a sub-class of net.rim.device.api.ui.Screen is created and pushed onto the Screen stack with pushScreen() (or one of its variants). To un-display a Screen, it can be popped off the stack manually with popScreen(), or it can be closed by calling the Screen's close() method. This is what happens to some Screen implementations (like the MainScreen) when the user presses the escape key. When the last Screen is popped off the Screen stack, the application will exit, though it is possable to change this behaviour to put the application in the background.

Unraveling Threads


We have alread discussed threads in enough detail for the moment when we discussed Events. So lets get on with programming.

Adding a MainScreen


There are many ways to add a new Class or Object to an existing project. We will be talking about each when we need to use it, for now let's use the traditional way of putting each Class in its own file so we create a BasicGUIScreen.java file and start typing. We will put it in the same package as our main object:

package src.lessonOne;

We also need to import the classes we will be using:

import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.component.LabelField;

And we can provide the universal program greeting:

public class BasicGUIScreen extends MainScreen {
 BasicGUIScreen() {
  setTitle("Hello World");
  add(new LabelField("Hello World"));
 }
}

To have our Screen displayed we simply modify the constructor of our main Class to create an instance of our Screen and push it onto the stack. We now have a program that does something we can see. Sitll not particularly useful, but it is a begining we can build on.

BasicGUI() {
 pushScreen(new BasicGUIScreen());
}

Here are the complete files, first BasicGUI.java:

/*
* BasicGUI.java
*
* © Richard Buckley www.hrbuckley.net, 2010
*
* This work is licenced under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
* To view a copy of this licence, visit http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to
* Creative Commons, 171 Second Street, Suite 300, San Francisco, California 94105, USA.
*/

package src.lessonOne;

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

/**
*
*/
public class BasicGUI extends UiApplication {
 public static void main(String[] args) {
  BasicGUI basicGUI = new BasicGUI();
  basicGUI.enterEventDispatcher();
 }

 BasicGUI() {
  pushScreen(new BasicGUIScreen());
 }
}

And BasicGUIScreen.java:

/*
* BasicGUIScreen.java
*
* © Richard Buckley www.hrbuckley.net, 2010
*
* This work is licenced under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
* To view a copy of this licence, visit http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to
* Creative Commons, 171 Second Street, Suite 300, San Francisco, California 94105, USA.
*/

package src.lessonOne;

import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.component.LabelField;

/**
*
*/
public class BasicGUIScreen extends MainScreen {
 BasicGUIScreen() {
  setTitle("Hello World");
  add(new LabelField("Hello World"));
 }
}

BasicGUI running on the simulator
Basic GUI Running on the Simulator

2 comments: