Monday, March 5, 2012

BlackBerrry Singletons, Application Context and Threads


Creative Commons Licence

This work is licenced under a Creative Commons Licence.

I wrote an an article on using singleton classes in BlackBerry OS applications. This, like many things, is a bit different on BlackBerry OS than other Java programming environments. While developing an application I ran into a concrete example of how the complex interplay of singletons, application context and the application event thread can cause strange results for the unwary.

My application adds fuctionallity to the BlackBerry Calendar application. Most of the action takes place under the hood in the back ground, but I provide a user interface for configuration and control that is accessed through an ApplicationMenuItem placed on the Calendar application menu. If this is the only time my application code needs to run there is a very efficient way to do it. Here is the core of the main application class (some details omitted for clarity):

//
//

public class Import extends Application
{
  public class CalendarMenuItem extends ApplicationMenuItem {

    public CalendarMenuItem(Object context,int order) {
      super(context, order);
    }

    // Push the user interface screen when the ApplicationMenuItem
    // is selected.

    public Object run(Object context) {
      UiApplication.getUiApplication().invokeLater(new Runnable() {
        public void run() {
          UiApplication.getUiApplication().pushScreen(new ImportScreen());
        }
      });
      return null;
    }

    public String toString() {
      return "Remote Calendar Import";
    }

  }

  private static long _ID = 0xabad1deaL;
  private static Import _instance = null;

  // Entry point for application
  // @param args Command line arguments (not used)

  public static void main(String[] args)
  {
     
    // Create a new instance of the application
    getInstance();
  }
    

  // Creates a new Import object

  protected Import()
  {        
    ApplicationMenuItemRepository.getInstance().
       addMenuItem(ApplicationMenuItemRepository.MENUITEM_CALENDAR,
       new CalendarMenuItem(this, 200));
  }
    
  // Get a singleton instance of Import

  public static Import getInstance() {
    if (_instance == null) {
      _instance = (Import)RuntimeStore.getRuntimeStore().get(_ID);
      if (_instance == null) {
        _instance = new Import();
        RuntimeStore.getRuntimeStore().put(_ID, _instance);
      }
    }
    return _instance;
  }
}

This application uses the RuntimeStore form of singleton. You don't see this used because I haven't posted those portions of the program. The singleton provides an easy way for other parts of the program to get access to runtime data and persistent data as required. Sharp eyed readers will notice that the main method does not eventually call Application.enterEventDispatcher(). Since this method is not called, main returns, the program exits and in the sense of application context, never runs again. When the user selects my ApplicationMenuItem my code runs, but in the context of the Calendar application. I don't need my own application context for anything.

If I only want my code to run on user command that is all I need to do, except write a killer application. However, the application is required to do some automatic processing in the background on a regular basis. A simple way to do this is to use the RealtimeClockListener. To do this I make the following modifications:
//
//

public class Import extends Application
    implements RealtimeClockListener
{
  //
  // Creates a new Import object

  protected Import()
  {        
    ApplicationMenuItemRepository.getInstance().
       addMenuItem(ApplicationMenuItemRepository.MENUITEM_CALENDAR,
       new CalendarMenuItem(this, 200));
    addRealtimeClockListener(this);
  }

CalendarListModel is a persistent singleton class:
  //

  public void clockUpdated() {
    Enumeration e = CalendarListModel.getInstance().elements();
    while (e.hasMoreElements()) {
      CalendarIdentification cal = (CalendarIdentification)e.nextElement();
      if (cal.needsRefresh()) {
        cal.addLogEntry(new CalendarLogString("Automatic refresh."));
        cal.refreshEntries(null);
        return;
      }
    }
  }

Compiling these changes into the application results in no interesting or useful behaviour at all. This is because when I register the RealtimeClockListener, it is registered within the application context of my application, which exits without calling Application.enterEventDispatcher(). When the application exits, this listener registration goes away. So I need to make one more change:
  //
  public static void main(String[] args)
  {
     
    // Create a new instance of the application and make the currently
    // running thread the application's event dispatch thread.
    getInstance().enterEventDispatcher();
  }

Now the automated processing works as expected. I just have to remember that when running from the RealtimeClockListener the code is running in my application context, but when running from the ApplicationMenuItem it is running in the Calendar application context. This is why I need to use either RuntimeStore or PersistentStore based singletons.

No comments:

Post a Comment