Introduction
I will begin with an introduction about myself. I have been working in mobile software development field for a number of years now, developing applications for pretty much most of the mobile platforms (J2ME, BREW and Android). I have wanted to start blogging for a while now, but didn't find time to get started. My topics for blogging will be anything that interests me, but for a number of years now I have been interested in mobile software development, so at least initially I will be blogging about that.
For my first post I want to talk about best practices for developing mobile applications that are portable across multiple handsets.
The guidelines are written primarily written from J2ME application development perspective, but you will see that they make sense across different platforms; the implementation mechanism for these platforms will of course be different. It also doesn't make any difference if we are developing a mobile game or an application, the guidelines apply to both (there are some significant differences between these two, for example asset management is a lot more sophisticated in games, but for current topic they are irrelevant and thus ignored).
In a typical company, an engineer (or two) will be assigned to create the first mobile application. He either has prior mobile development experience or maybe he doesn't. In any case he will do whatever it takes to get the application ready. Now hopefully everyone in the company likes it and want it ported to 10+ different handsets ASAP so that they can do a commercial launch. There may be other scenarios about how this application came exist, they don’t matter much here. What matters is that a mobile application that was developed for one mobile handset is now being asked to be made portable to support 10+ (and eventually a lot more) handsets.
Herein lies the problem, that the application wasn't designed to be portable.
Whatever the reason, we now need to make the application portable. These guidelines I am going to talk about, if followed during the application development process, will facilitate porting. They can be also followed after initial development to make an application portable.
Keep the application design modular
This will come as a no brainer (with almost all software best practices recommending modular design) but the application logic, custom UI layer and platform specific API calls should be modular. This way, it would be very easy to create new applications just by changing the application logic. The custom UI layer will have custom UI controls and custom fonts, but some low-end handsets (the so called feature phones) don’t have enough handset memory to support them, so we can swap them out with native UI (Native J2ME controls etc) easily if the application has modular design. Now this doesn't mean that both native UI and custom UI layer need to be created during initial development, just that the design should be modular enough to support these kind of changes.
Most established mobile companies have reusable frameworks that they use to rapidly create mobile applications and games. Most of the time, these frameworks have evolved from these UI and platform specific modules. Thus this initial effort to keep the design modular pays off many times.
Classify handsets into categories
Most mobile development companies have a classification system for handsets, usually called something like high-end, medium-end and low-end categories. They use various parameters to decide which category a handset belongs. Most of the time, the handset screen size is an important parameter, primarily because it is harder to package image assets of multiple screen sizes into one build.
So, it is important to classify handsets into a number of categories and create builds for each category. Having said that, this exercise can be very time intensive (most companies have a set of applications that take are run on a given handset that help determine which category they belong to). However, it is very easy to create classes of handsets based on screen sizes and start development; they can be modified later without a lot of effort. So at the beginning, we can create say, large, medium and small screen size categories and create builds for just these three. This will allow us to design the application so that it facilitates this kind of classification and thus allow multiple handset support.
For example for J2ME applications, we can create builds for say, the following screen sizes.
A. 176x220
B. 240x320
C. 240x400
D. 320x480
E. 360x640
F. 480x800
Notice that we don’t have to stick to only 3 categories. Now having created builds for these screen sizes, it will be trivial to port to handset having these screen sizes (most of the time there are no code changes required).
Sometimes the annunciator bar at the top takes say 20 pixels (and cannot be removed by api calls like setfullscreenmode) and 240x320 screen size now becomes 240x300. To support this new screen size we should design the mobile application so that the 240x320 screen size takes a jad parameter that will allow us to offset height by say 20 pixels and draw the display. It has to be designed to support this as it will curtail proliferation of builds based on minor differences in screen sizes.
Note that with proliferation of touch screen handsets it is a good idea to create a touch screen build for each screen size greater that 240x320.
Design mobile applications with a housekeeping tasks module
It is a good idea to design mobile applications to perform certain housekeeping tasks when the application is first launched after download. It should be designed to allow this, with a module just for initial housekeeping tasks. These tasks can be varied and change for different types of mobile applications, but some of the more common ones are detailed below.
Nowadays most applications require server connectivity, so on first launch after download it should be able to establish server connection and identify itself. This identification step is very important, and, depending on requirements can be as simple as just creating and sending a handset ID (based on user agent string, or a prepackaged handset ID in app) or a user ID (based on phone number, or some other unique string creation mechanism) or a sophisticated authentication mechanism.
Note that it is quite feasible and in some cases recommended that the user ID is generated on server and sent to handset when the application is downloaded and first launched. It then saves this user ID on handset (in RMS or config file in file system or some other similar mechanism). This user ID is used on all subsequent network calls to identify user to server, thus greatly enhancing reporting, usage tracking and even in customer support issues.
Also, it may be helpful if we have the mobile application make a server call in background each time it is launched. This functionality can be very useful. It can allow us do any initial housekeeping tasks or put the application in debug mode based on a debug flag sent from server to troubleshoot customer support issues. It can also allow on the fly customization of menus and UI. In well designed applications it is even possible to change the look and feel of the app, with all UI assets being downloaded when the application is launched (a detailed look at server interaction for applications is worth an entire blog post in itself, which will be coming in future). In any case a housekeeping module will be greatly helpful for tasks like these.
Landscape mode support
Except for in handsets with small screen sizes like say 176x220, the mobile application should have landscape mode support built in. It should support portrait and landscape mode, with all assets required for that packaged in into the build (API calls like
sizeChanged(int w, int h) in J2ME let the application know about portrait to landscape mode changes). Obviously this should also be done for touch screen handset builds.
There is an important aspect to this. Suppose a handset of screen size 240x320 requires landscape mode support. Now since we have designed our application to support landscape mode, it should work without any code changes. Now, there is another handset of screen size 320x240 that needs to be supported (requiring landscape support too). The 240x320 build should work on this handset too, the application internally figuring out what assets to use in portrait mode. To do that, we need to provide a mechanism in the application, to specify the screen size of portrait mode. In J2ME, this can be done as a jad parameter. The application can then read this value, and use it as default portrait mode (one way is to have all assets in
width x height directories in jar, and the code accesses the assets as
$jad-parameter/imagename.png). Using this mechanism we can use 240x320 build to work on a handset with screen size 320x240, without worrying about portrait/landscape mode confusion.
Another way to address this is to make a utility method that internally determines what screen size assets to use. This method is called to get the assets directory name in code to display the image.
We should also provide a mechanism to disable landscape mode with, a configuration parameter like a jad parameter, to be used in handsets that don’t need landscape mode support. It can be argued that this is not required, that the application should be able to handle this internally, but I prefer this as it provides me a mechanism in code, to disable landscape mode support explicitly.
QWERTY keypad, virtual keypad and 4x3 keypad support
If any mobile application requires text entry, then it should support text entry from a QWERTY keypad, virtual keypad (on touch screen handsets) and 4x3 keypad. If any special characters need to be supported, that application design should incorporate that. One another thing to keep in mind is to ensure that this design should be flexible to allow addition to this list of characters; they should not be hardcoded (details about this design will be in a future post).
Applications should support both native and custom UI controls
All mobile platforms provide native UI controls like text box, menu etc to use in mobile applications. And a lot of them are inadequate. For example the native UI controls in BREW and J2ME are notoriously hard to customize. For this reason it is a good idea to create a library of these controls, with the flexibility we need, to use in our apps.
The same rule applies to fonts, it is a good idea, even advisable to create and use our own fonts. Now most font files are in PNG format, make sure that you create them in indexed PNG format as they reduce the file size significantly.
Customer Service web page
Most applications save usage and reporting data on server and so, to help debug customer service issues, a web interface for this data should be created that will allow us to view and modify it. This is a relatively easy thing to do and this web page need not be on internet, but it pays of many times over.
Design the application to facilitate localization, even if we know that it will never support another language.
All user visible strings and image names should be in one central place, ideally loaded as a resource or a language file (like say
US_en_copy-strings.txt for english language). If that is not possible (J2ME doesn't support resource bundles) then in a text file, or a java class. Thus it will facilitate support for localization as then we would just need to swap out these files. Also if we need to do copy changes or change an image name we don’t need to search for it code and do so.
Logging module
All mobile applications should have basic logging built in, that is disabled by default, but can be enabled from server to display debug messages or enabled in a debug build.
Nightly builds and Release Management System
All applications being actively developed should have a nightly build, which compiles all the configurations. The application framework, if it exists should also have its own nightly build. This pays off exponentially, most people dont see the value of this system until it exists and then they dont know how they did without it.
There is one more benefit of this kind of continuous integration system, it can be easily modified to create a Release Management System where all release builds are created from the release server, not from developer machines. This removes any inconsistencies that may be caused by developer environments and standardizes the release process.
These guidelines are by no means complete, being primarily based on what I learnt over the years, but are good enough for a start. Any feedback on this topic is very much appreciated, the goal of this post just to start this discussion.