The 10 Biggest Mistakes Developers Make with QUALCOMM BREW
Despite everyone's efforts to the contrary, all of us tend to make the same mistakes, sometimes over and over again. It's instructive to occasionally stop and look at the mistakes that our peers or we make, because doing so brings them into clearer focus so that perhaps we'll be less likely to repeat them in the future.
In this article, I summarize the top ten mistakes I've observed both new and practiced developers make when writing applications for the QUALCOMM BREW platform in C or C++. This is by no means an exhaustive list, and I suspect that some readers will likely have favorites that may not be on this list. However, I think it's representative of the common traps and pitfalls most BREW developers first stumble across and then learn to avoid.
10. Failing to Check Return Values
Nearly all BREW interface methods return values indicating the success or failure of the method call. Most have very fine-grained error reporting, using a bevy of values defined in aeeerror.h to indicate the source of a problem (whether developer misuse, insufficient resources, or another issue). However, many developers fail to check these return values, making it difficult or impossible to determine the source of errors (and often subsequent crashes). This is particularly a problem in areas that aren't wholly under programmatic control, such as when interacting with the file system (which can be full) or the network (that can disappear unexpectedly at any time). Always test return codes, and always define some sort of error handling. Even a single screen that reports that an unexpected error has occurred and terminates the offending application is better than a random crash.
9. Setting Very Short Timers, or Just Using ISHELL_SetTimer Instead of ISHELL_Resume to Defer an Operation
BREW provides two ways to let you schedule the deferred execution of a function: ISHELL_SetTimer and ISHELL_Resume. You should only use ISHELL_SetTimer when you have some interest in trying to ensure that the function you want to execute should have its execution delayed by some clearly defined interval. Although ISHELL_SetTimer doesn't guarantee that the interval you specify will be honored, it will do its best to do so, and always ensures that it won't invoke your function in less time than you specify. ISHELL_Resume, on the other hand, invokes your function later—typically just before or after your application receives its next event. Because ISHELL_Resume requires you to have an AEECallback stored on the heap (not on the stack, which will cause an immediate crash!), many developers prefer instead to use a very short-duration timer to defer execution of a function, which is both inefficient and crash prone. It's inefficient because the timer mechanism typically defers execution for tens or hundreds of milliseconds after registering a new timer, and crash prone because some handsets simply will crash if you attempt to schedule timers with intervals below some well-observed threshold (typically around 50 ms).
8. Being a Resource Hog
Although BREW-enabled handsets have matured significantly in the last eighteen months (megabytes of heap and multi-hundred megahertz processors are fast becoming the norm), they're not nearly as powerful as desktop computing platforms. Take great care to allocate only the memory or interfaces you need, and ensure you release all resources in a timely manner (including at application suspend and exit). This is especially important for memory access when you may have lots of small memory regions, because heap fragmentation can still be a problem, especially on older devices.
7. Freeing Already Freed Pointers or Releasing Already Released Interfaces
This is a major issue on any platform without garbage collection, and QUALCOMM BREW is no exception. You should take great pains to never free a pointer (or release an interface) twice—I like the habit of nulling pointers and interfaces after they're freed, and checking to see if they're null before freeing (or releasing) them. Closely related to this is abusing—or misunderstanding—BREW's reference counting mechanism for interfaces. Put simply, if you're using an interface that someone else gave you, you should always AddRef it to ensure the entity that provided it doesn't release it. When you're done, you should release it. Of course, if you're passing an interface to another method—say, providing a stream to IIMAGE_SetStream—you should be sure to release the stream, because you're done with it at that point and it's now the IImage's responsibility to release the object when it's finished. You can almost always recognize code written by folks who don't understand the BREW reference counting mechanism (or by those who have not considered reference counting in their design) by seeing bits of code like this throughout their application:
while( IBASE_Release( (IBase *)pInterface ) ;
6. Failing to Update the Screen (or Updating the Screen Too Much) Using IDISPLAY_Update
This one seems to catch developers new to BREW more than experienced developers. In the first scenario, your application mysteriously fails to draw things on the screen, even though all the requisite code is in place. After arduously checking the coordinates of each draw operation in calls to the various IDisplay, IGraphics, and IImage interfaces, you decide to throw in a call to IDISPLAY_Update, and suddenly everything works! Once you make this discovery, it becomes very tempting to simply call IDISPLAY_Update (or IDISPLAY_UpdateEx, passing TRUE to force an immediate redraw) any time you feel the screen should be redrawn—which will slow your application considerably. The rule for IDISPLAY_Update is simple: invoke it once when you're finished with all drawing operations. This schedules a screen repaint with the underlying runtime at some (hopefully soon) time in the future. If you're really concerned that the redraw must happen immediately, you can force an immediate redraw by using IDISPLAY_UpdateEx, passing TRUE as the second parameter to force an immediate redraw. However, bear in mind that abusing this won't make your application faster, but will actually slow your application down because the system will spend more time repainting the screen than running your application.