July 28, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

BREW & J2ME: Let's Be Friends!

  • December 4, 2003
  • By Radu Braniste
  • Send Email »
  • More Articles »

Design

Our declared goal is to ease the creation of "high-level interface" code and associated logic, making life easier for Java developers. This means that, theoretically at least, we could be able to write code like this:

List* l = new List("Title");
l->setCommandListener(myListUsage);
String a[] = {"1","2","3","4"};
l->append(a);
Display.getDisplayable()->setCurrent(l);:

As we could see in the overview, there are various forces influencing the design. Here are some examples in no particular order: no built-in garbage collector; no static variables = no singletons in the sense of [2]; no root Object but safe type generics, and so forth. Considering all these factors, it becomes clear that code as above cannot be written freely in BREW. Reasons might be:

  1. There is no List.
  2. 'new' has to paired by 'delete'.
  3. There is no string or wide string available.
  4. Placing an array of AECHARs directly on the stack might be dangerous.
  5. There are no listeners.
  6. Singletons are difficult to implement.

Providing the best possible solutions to problems of this kind is what BREW_J2ME framework does.

One of the most important decisions is related to memory management and objects lifetime. If there is no delete, who's responsible for the destruction of our widgets? There are obvious answers, such as stack-based objects and smart pointers for a scope-defined life span or reference counting (that happens to coincide with BREW solution). One solution, used in previous articles, is to register objects and bind their lifetime to the life of the registrar. This implies an additional layer (the registry) and has the advantage of keeping track of all the resources in one place. There is another subtler advantage. too. A C++ application created directly on top of BREW is not a first-rank C++ object but merely a POD structure. That's why real C++ manipulation explicitly requires such an insulation layer. We will call our registry DisplayableRegistry.

Our registry will mainly keep track of displayable resources, implementing IDisplayable interface:

struct IDisplayable
{
  virtual bool onCmd() = 0;
  virtual bool containsItem(int idx) = 0;
  virtual int getID() const = 0;
  virtual IControl* getControl() const = 0;
  virtual ~IDisplayable()
  {}
};

An IDisplayable is somehow equivalent with an IControl in BREW, even if it exhibits additional functionality.

For convenience, every application inherits from an IMidlet abstract class, responsible for declaring and implementing dummy application-level event callbacks (this subject will be discussed later), as well as providing access to the unique instance of DisplayableRegistry. This solves another issue—Singleton implementation. Please note that the mechanism supplied by IMidlet and dynamic polymorphism is not necessary, per se—static polymorphism can do the trick, too. Making every component aware of the application is somehow equivalent with giving them a context.

class IMidlet
{
public:
  DisplayableRegistry* getDisplayable() const
  {
    return rr_;
  }
  void setRegistry(DisplayableRegistry* rr)
  {
    rr_ = rr;
  }
  virtual bool onStart()
  {
    return true;
  }
  virtual bool onStop()
  {
    return true;
  }
  virtual bool onSuspend()
  {
    return false;
  }
  virtual bool onResume()
  {
    return false;
  }
  virtual ~IMidlet()
  {}
private:
  DisplayableRegistry* rr_;
};

This is a possible implementation of DisplayableRegistry:

class DisplayableRegistry
{
public:
  int registerResource(IDisplayable* resource)
  {
    for(int i = resources_.size() - 1; i >= 0; --i)
    {
      if (resources_[i] && resources_[i] == resource)
      {
        return i;
      }
    }
    resources_.append(resource);
    return resources_.size() - 1;
  }

  bool unregisterResource(int uid)
  {
    if (resources_[uid] )
    {
      delete resources_[uid];
      resources_[uid] = 0;
      return true;
    }
    return false;
  }
  IDisplayable* getRegistered(int uid) const
  {
    return resources_.isEmpty() || ((resources_.size()-1) < uid)
                                   ? 0 : resources_[uid] ;
  }
  void setCurrent(IDisplayable* resource )
  {
    setCurrentImpl(resource);
  }
  int getNextAvailableID() 
  {
    ++itemID_;
    return itemID_;
  }
  bool onCmd(int itemID, long data) const
  {
    int id = INDEX_OUT_OF_BOUNDS;
    for(int i = 0, sz = resources_.size(); i< sz; ++i)
    {
      if (resources_[i]->containsItem(itemID))
      {
        IDisplayable* d = resources_[i];
        return resources_[i]->onCmd();
      }

    }
    return false;
    
    Midlet* getApp() const
  {
    return m_;
  }
  bool isHandled(AEEEvent eCode, uint16 wParam,
                 uint32 dwParam) const
  {
    for(int i = 0, sz = resources_.size(); i< sz; ++i)
    {
      IControl* c = resources_[i]->getControl();
      if (c && ICONTROL_HandleEvent(c,eCode, wParam, dwParam))
        return true;
    }
    return(false);
  }
  ~DisplayableRegistry()
  {
    delete m_;
    unregisterResources();
  }
  DisplayableRegistry(IMidlet* m):itemID_(100), m_(m)
  {
    m_->setRegistry(this);
  }
private:
  void unregisterResources()
  {
    for(int i=0, sz = resources_.size(); i < sz; ++i)
    {
      delete resources_[i];
      resources_[i] = 0;
    }
  }

  void eraseAll() const
  {
    for(int i=0, sz = resources_.size(); i < sz; ++i)
    {
      IControl* c = resources_[i]->getControl();
      ICONTROL_SetActive(c,false);
    }
    IDISPLAY_ClearScreen(getDisplay());
  }

  void setCurrentImpl(IDisplayable* resource)
  {
    eraseAll();
    ICONTROL_SetActive(resource->getControl(),true);
    IDISPLAY_UpdateEx(getDisplay(), false);
  }
private:
  int itemID_;
  IMidlet* m_;
private:
  DisplayableRegistry( const DisplayableRegistry &value );
  const DisplayableRegistry &operator =
        ( const DisplayableRegistry &rhs );
};

And, finally, making the registration process as well as the GUID-based BREW initialization process transparent requires an additional construct—a factory. Our first line of code is now:

List* l = List::getList("Title", this);




Page 2 of 4



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel