The Lurker's Guide to Optimizing MIDP apps (2002-7-7)
The Java ME (J2ME) environment will probably comes as a shock to many developers who come from JEE backgrounds. Instead of hundreds of megabytes of RAM and gobs of program memory, in the MIDP/CLDC (to take an example) you can be limited to as few as 160 kb of TOTAL memory! At this point, a feeling of hopelessness about the situation may engulf you, and you might have the urge to jump back to the relative safety of your beloved 1.2 GHz servers. But don't fret, intrepid java developer, we'll go over some of the things you can do to optimize your code for MIDP apps, and before you know it, you'll never want to touch your lumbering Websphere app server again (well, maybe not).
Optimization can of course, mean many things, but in our context we'll define it as designing and developing MIDlets such that they can efficiently exist in the constrained environments of limited devices while placing minimal strain on the available resources.
We can distinguish between different types of optimization:
In JEE environments, where portability and maintainability over the long run is frequently a concern among developers (or at least, it should be), the organization, structure, and readability of the code is of paramount concern, and OOP is the holy grail that should never be broken.
Thus, JEE developers who start writing MIDP code frequently fall back into their regular patterns. Objects proliferate like crazy, and no inheritance scheme with multiple interfaces is taboo so long as it advances the goal of long term maintainability and app flexibility.
Unfortunately, the constraints imposed on MIDP apps by the environment means that developers be more careful about unnecessarily creating many objects. In fact, you should avoid whenever possible creating objects that cannot be reused like Strings. Objects are allocated from runtime memory, a scarce resource that should not be squandered so easily, unlike primitive data types such as int, boolean, and char, which are allocated directly on the stack.
One common way to avoid creating unnecessary objects is to use StringBuffers instead of Strings.For example, instead of:
Use a StringBuffer instead:
Another way in JEE to reuse objects is to make use of so-called object pools, when the creation of "expensive" objects is kept to a minimum by storing already-created objects in pools, where they can be reinitialized and reused when needed. Note though, that caching objects when they aren't always needed may actually cause problems in Java ME (J2ME) and lead to the dreaded Out Of memory error.
In summary, although maintainability through the correct use of OOP is still possible in MIDP programming, allowances must be made for the resource constrained environments that the MIDP app will find itself in.
Optimizing for Speed
Optimizing for speed means following coding practices that will speed up the execution of the MIDlet code. This is probably one of the more important points to consider since consumer devices like cellphones frequently have relatively slow processors.
Before going into the nitty gritty, I'd like to point out one thing: the end user's perception of your app's speed is usually a lot more important than fine-tuning the heck out of your code to shave out that last millisecond of loop time.
For example, having the app "freeze" or respond sluggishly when the end-user expects a response after he/she pushes a button is unacceptable. If your app takes some time to do something (e.g. it may take time to connect to an external server and receive the response), don't just let it seem to "freeze". For example, you could introduce a graphic/message in the UI that denotes to the user that some activity is taking place in the background. A clock could be one such image, or a progress bar counting down the time.
There are several code optimization techniques that will help speed up your MIDP apps.
The following code calls the Vector's size() method everytime it loops. Depending on the number of loops called, this could have an effect on the speed of execution of your app code.
Instead, calculate the size of the Vector before the loop.
Vector and Hashtable are easy to use, but do include some overhead. If you decide to use them, then you should try to size them correctly when first created. If you do not size them correctly, resizing either one when it becomes necessary later will involve some expensive processing time.
It takes slightly longer for Midlets to access instance variables then local variables. For example, instead of accessing an instance variable multiple times from inside a loop, set a local variable to the instance variable, then use the local variable in the loop.
Optimizing for Size
Ok, you've created the world's greatest MIDP app. It's got all the features you think would make it one cool app for any user, and you've added tons of additional packages like XML parsers, cryptography, and other such supporting classes in order to create your uber-MIDlet.
But, wait! Holy cow, Batman! It turns out those dastardly phone manufacturers have imposed a size limit for apps installed into their phones! You groan in disbelief as you realize that squeezing a 100 kb jar file into the 50 kb size limit requires some more ingenious coding on your part.
One thing that you should always keep in mind is that the restrictive natural environment of your MIDlet means that optimizing for size is one of your most important tasks.
Here's some tips:
Break up extra packages that you had simply dumped into your directory during the developmental stage, such as packages for XML parsing. The extra time it takes to do this might make the difference between a viable MIDlet and one that cannot be commercially deployed.
Prune unnecessary functionality from your app by leaving out some classes. Try to get rid of "bells and whistles" functionality that may not add anything essential to the whole.
Refrain from using inner classes, and especially anonymous inner classes. In addition to incurring a certain amount of overhead due to the additional classes, the compiler generates additional methods and variables that allow the inner classes access to the private variables of the class enclosing it.
Obfuscating your code results in the side effect of reducing total size because the obfuscator renames your descriptive method and variable names with much smaller machine-generated names. The Java ME (J2ME) wireless toolkit from Sun (version 1.04) is able to obfuscate your code. Just download the retroguard.jar file from www.retrologic.com, then place it in the bin subdirectory of the main toolkit folder. Then, simply select "Create Obfuscated Package" when creating the final jar.
Images can add a lot of to the total MIDlet size, and some time spent looking over the images you want to include in the MIDlet suite might pay good dividends. Only include images that you feel are necessary to the app, and keep the number of colors down to reduce the file size of each image. Since many of the target devices can display only a few colors per pixel, creating images with many more colors per pixel may not add anything of value to your app. Finally, reducing the actual dimensions of each image will also reduce its file size.
Another way to get around the image size problem is to request the image from the server when necessary. Of course, this is probably not advisable because (1) it may create new problems of long downloading times for the enduser and more importantly (2) it necessitates that the enduser be able to connect to the server, something that is probably not the case for the majority of endusers.
That's it! BTW, I'd be happy to hear from you regarding other tips on how to optimize code. In the meantime, happy optimized coding to all ye!
Copyright © 2006 RimLife Technologies LLC|
All Rights Reserved. Java, Java ME (J2ME), are the trademarks of Sun Microsystems Inc.