Thursday, January 29, 2009

Developing mobile applications using Gear episode 7: Work with EventManager and async messages

With this tutorial we're going to learn the basic concepts behind EventManager class and how we can send asynchronous messages across our Midlet with Gear Java Mobile Framework. To better understand this guide, we suggest you to read at least this post about configuring Eclipse and to download Gear's latest release (1.2.0).

1.0 EventManager:

Working with Gear in most cases requires to use EventManager class to dispatch asynchronous messages to other Objects within your MIDlet. It's implemented following the Singleton pattern so there will be only one instance of it during execution time and you can reference it wherever you want inside your code via public method getInstance(). Let's go on how to use this class and its methods.

1.1 Events

Every message you want to be forwarded through EventManager must derive from the abstract class Event. It contains the basic structure and methods to keep track of the sending Object and category. Here's an example:
public class ConnectionClosed extends Event {
// Public constructor with empty EventArgs object
public HideMidlet(Object sender){
super(sender);
}
// Public constructor with user defined EventArgs
public HideMidlet(Object sender, ConnectionClosedEventArgs eventArgs){
super(sender, eventArgs);
// Override of getCagegory method to return the desired type
public Category getCategory() {
return Category.APPLICATION;
}
}
1.2 EventArg

As you noticed in last example, you can assign some custom defined arguments to an event to pass on additional data to the receiving Object. To achieve this, you have to extend EventArg class and implement your own private fields along with proper getter and setters. Here's an example:
public class ConnectionClosedEventArgs extends EventArg {
private ConnectionStatus connectionStatus;

public ConnectionClosedEventArgs(ConnectionStatus connectionStatus) {
super();
this.connectionStatus = connectionStatus;
}

public ConnectionStatus getConnectionStatus() {
return connectionStatus;
}

where ConnectionStatus is a user defined class containing additional information about the reason for connection termination.

1.3 Dispatch events

This task is pretty simple. Suppose Object A wants to send some message to Object B (we'll see on the next poit how B will actually receive it). All it have to do is to obtain a reference to EventManager and enqueue the desired message, wrapped inside an Event object.
EventManager.getInstance().enqueueEvent(new MyEvent(this))
That's it. In this particular case, we're sending a MyEvnet (which derives from Event class) and we don't have to care about anything else. EventManager will internally manage our request and dispatch it. Notice we passed "this" parameter to the Event constructor to allow a reference of the original sender to be kept.

1.4 Register and receive Events

In order to receive an Event, an Object must first implement an interface named EventHost and thus it's only method
public void notify(Event event);
and secondly register itself to EventManager by calling
public void registerHost(EventHost host, Event.Category category)
where the second argument is one of the categories mentioned before. A class that is registered to at least one Event.Category will be notified of incoming messages. Whenever you need to unregister an Object from EventManager you can call
public void removeHost(EventHost host)
to totally remove it from the queue or
public void removeHost(EventHost host, Event.Category category)
if you just want to stop being notified for a specific category of events.


1.5 Events and GearMIDlet

By default, GearMidlet register itself to APPLICATION and GRAPHICS categories inside its base constructor, but if you need to be aware of more Events or less you can register/unregister from EventManager as seen in previous sections.

2.0 Putting it all together

With all the concepts from previous section, we can now create a GearMidlet with full support for asynchronous messages:


public class GearTouchDemo extends GearMidlet {

protected boolean onDestroy() {
return true;
}

protected void onPause() {

}

protected void onStart() throws MIDletStateChangeException {
// Enqueue a request to display MainMenu user interface
EventManager.getInstance().enqueueEvent(new DisplayWidget(this, MainMenu.class));
}

public class MainMenu extends GWGrid {
public MainMenu(){
super(2,2);
addItem("Photo browser", "/icons/c1.png");
addItem("Drops", "/icons/c2.png");
addItem("None", "/icons/c3.png");
addItem("Nothing here", "/icons/c4.png");
setTitle("Main menu");
addCommand(CommonCommands.SELECT);
addCommand(CommonCommands.EXIT);
setCommandListener(new MainMenuCommandListener());
}

public void itemClicked(ImageItem clickedItem) {
if (commandListener != null){
commandListener.commandAction(CommonCommands.SELECT, this);
}
}

public void selectElement(){
switch (getSelectedIndex()){
case 0:
// Enqueue a request to display MainMenu user interface
EventManager.getInstance().enqueueEvent(new DisplayWidget(this, PhotoBrowser.class));
break;
case 1:
// Enqueue a request to terminate the MIDlet.
EventManager.getInstance().enqueueEvent(new QuitEvent(this));
break;
default:
displayAlert("Function not implemented");
break;
}
}

}

No comments: