BREW Up a Better Way to Synch Data Between Apps Using a Singleton
The most elegant way to ensure the synchronization of data between two separate apps is to use a singleton. The problem is that BREW doesn't support global or class member variables. Learn how BREW's IModule interface lets you implement singletons—without relying on the usual methods.
As a BREW developer, you're probably intimately familiar with the process of writing applications, but perhaps not as familiar with the process of writing extensions. In BREW, an application is a class that can only have one instance. This means that a BREW application is a singleton, an example of a class which ensures that only one instance of the class can be created (for an in-depth examination of the singleton design pattern, see Design Patterns, by Gamma, et al.). Occasionally, however, you may need to create a BREW extension, often to manage shared resources or a shared interface: a good place in which to use a singleton. For example, say you have a private extension between two applications that controls access to data in /brew/shared on the file system. In a case like this, you want to ensure that the extension accurately represents the state of the file system; if both applications are running (say one in background and one in foreground mode), a singleton can greatly simplify synchronization.
The typical patterns for implementing a singleton require either a global variable or a class member variable. Unfortunately, neither of these constructs is available in BREW when compiling for Over-the-Air (OTA) provisioning. This has driven many developers to experiment with the local thread storage API available in later versions of BREW, bizarre tricks like writing pointers to active data in memory to configuration files, or simply to despair.
Fortunately, there is a better way: leveraging the nature of BREW's IModule interface, which itself is a singleton enforced by BREW at creation time and using it to implement the singleton contract. To do this, you must first understand a little about how BREW instantiates components.
The Lifecycle of a BREW Component
Before just subclassing BREW's IModule interface in the hopes of conjuring up a singleton, it's important to understand exactly for what a BREW IModule is responsible. Unfortunately, the existing documentation in this area is fuzzy at best. BREW modules—such as applications and extensions—are the fundamental unit of code loading. In BREW, a module can contain the implementation for multiple classes, such as an application or multiple extensions. Your module is accompanied by a module information file, which provides information regarding the classes your module contains. When BREW loads a module from disk, it represents the module with a unique instance of the IModule class. During execution, this IModule instance is a factory, creating instances of the classes it contains when requested by BREW or other applications. Once all instances of these classes in the module are released, the module itself is released, freeing the memory it used to contain its code and data.
Using the IModule Interface
Normally, you don't have to worry about the implementation of IModule. QUALCOMM BREW provides a helper file, AEEModGen.c, which contains reference source code for modules. You implement a global function, AEEClsCreateInstance, which the AEEModGen.c file invokes in a timely manner when loading your component. Internally, however, AEEModGen.c implements AEEMod_Load, which is the entry point for all modules (a quick peek at QUALCOMM make files confirms this, too—the make file instructs the linker to ensure that AEEMod_Load is the first function in the compiled module). AEEModGen.c also implements the four methods of IModule:
A little-known fact makes the implementation of a singleton quick and easy: BREW only loads and creates a module once—regardless of how many times it is required. This makes sense, because loading a module is an expensive process—it requires updates to system tables about what classes are available and also brings in the executable from disk and allocates ancillary support structures. By implementing your class that implements the IModule contract, you can add support for singletons, class variables, or anything else you want to ensure only exists once per module. This is a little trickier than simply writing a normal BREW class, because instead of just writing a class and its methods, you must also write the module load function.
The IModule Subclass Representation
The code below creates the data structure that represents the IModule subclass instance. But first, take a look at the in-core representation of the singleton’s IModule subclass:
typedef struct _SSingletonModule
/// Our singleton pointer.
</code> This is pretty basic stuff if you've built extensions before. The first line declares the virtual function table for this class, which implements IModule. The next lines are the typical baggage carried by all classes—a reference count and a pointer to the system shell. Next are two function pointers required for static extensions (those extensions compiled in-line with the ROM), but unused for OTA applications. <table style="background: white none repeat scroll 0% 50%; -moz-background-clip: initial; -moz-background-origin: initial; -moz-background-inline-policy: initial; font-family: Verdana,Arial,Helvetica,Sans-Serif; font-size: x-small; color: red;" align="center" border="1" cellpadding="3" width="95%"> <tbody><tr><td> Author's Note: I choose to keep these (and code you see in later listings) for static extensions in all of my code, because I've been fortunate enough to work in settings where I've needed to quickly port existing code to work in ROM for handset manufacturers.</td> </tr></tbody></table>
Finally comes the secret sauce: the last line will contain a pointer to the singleton class when it's created.
The IModule Entry Point
Next, you need to write the entry point for the module. This isn't hard, both because there's a reference implementation in AEEModGen.c and because with one notable exception, it's the same as the first-stage constructor for other classes (Listing 1).
This code has two interesting chunks: the plethora of preprocessor directives in declaring the entry point (the first eighteen lines) and the simulator-specific bits in AEEStaticMod_New. These are necessary because the manner in which the module is loaded depends on the platform:
The IModule Subclass Method Implementations The AddRef and Release methods for the new IModule class are just like any other AddRef and Release, so they're not shown here. The FreeResources is an empty function, too, since there's no special file or memory allocation on a per-module basis that needs to be tidied up. What remains is CreateInstance, the method BREW invokes in the module to obtain class instances from the module. It's what calls the class' AEEClsCreateInstance function. The module's CreateInstance functionm must do the same work as the existing IModule's CreateInstance as well as enforce the singleton contract (see Listing 2).
The function shown in Listing 2 has two jobs: to enforce the singleton contract and to create the singleton instance if one is not already available. The code is quite straightforward. On entry, if the module is being asked for an instance of the singleton class and the module's pInstance member has a pointer to the singleton class, it simply increments the reference count for the existing object and returns it. This guarantees that any client of the module always receives the same instance of the object. Otherwise, it creates the object in the usual way, either by calling the static-compiled factory method if one was handed to AEEStaticMod_Load, or by invoking your AEEClsCreateInstance (which is the usual case).
The rest of the singleton extension is coded just like any other extension: its entry point is CreateInstance, and it is responsible for building the public interface with its virtual table structure, and private data structure that's cast-compatible with the public structure.
As you can see, it's easy to create a module that can implement the singleton contract for one or more of its classes. While the example I show demonstrates a single class, you can extend this easily by adding additional slots in the module's data for each singleton class instance, then adding the appropriate enforcement in your module's CreateInstance method.
In addition to allowing you create singletons, the knowledge that the IModule instance for your module is only created once gives you the opportunity to share other data between class instances. This gives you an effective way to create variables with class-wide scope. The possibilities for this are virtually limitless, from sharing data between class instances to pooling shared resources for compression or other algorithms.
Ray Rischpater is the chief architect at Rocket Mobile, Inc., specializing in the design and development of messaging and information access applications for today's wireless devices. Ray Rischpater is the author of several books on software development including 'eBay Application Development' and 'Software Development for the QUALCOMM BREW Platform,' both available from Apress, and is an active amateur radio operator.
<input src="http://www.devx.com/assets/devx/9827.gif" name="emailAuthor1" onclick="sendEmailAuthor1();return false;" border="0" height="21" type="image" width="101">
Последний раз редактировалось Mihalych, 02.08.2004 в 11:25.
|Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)|
|embedding j2me apps for chinese market||Mixa||Мобильный дайджест||0||30.05.2006 13:58|
|3D игры Brew||Lifern||Brew||19||20.12.2005 19:30|
|Brew программист||sbe||Поиск работы||2||11.08.2005 21:10|
|install brew||BaToH||Brew||0||17.02.2005 20:18|
|Sun paves way for Java enabled mobile apps||Mihalych||Мобильный дайджест||0||06.07.2004 12:45|