Mobile Java: The Next Generation
When you compare the lifecycle of Java ME Mobile Information Device Profile (MIDP) applications to Android applications, differences stand out.
Figure 2 is the lifecycle for a MIDP application (a "MIDlet"). A typical mobile phone then might have a black-and-white 96×54 pixel screen, with 128 KB of RAM and 256 KB of nonvolatile memory. In such a constrained platform, only one MIDlet executes at a time. A MIDlet spends most of its lifecycle shuttling between two states
active, where it does work, and
paused, where the phone switches the MIDlet out so that it can handle a call. However, a MIDlet isn't completely inactive while in the
paused state. It can use timers to schedule work, and create background threads to execute tasks that don't require the UI.
One problem when writing a MIDlet is that its
startApp() method is called every time the phone returns it to the
active state. This complicates the MIDlet's design because you often place the initialization code in the
startApp() method. The reason for locating code there (rather than in the MIDlet's constructor) is that the specification requires that if an error occurs in the constructor's code, the stack throws a fatal exception. This behavior rules out the possibility of recovering gracefully or displaying messages that could tell users what went wrong. Many MIDlet applications therefore set a Boolean flag so that for subsequent calls of
startApp(), the initialization code is bypassed. Another complication is that there's no set mechanism to preserve the state of a complex MIDlet when it is paused. You must save the values of key variables into its RMS yourself when
pauseApp() is invoked, then restore these values when the MIDlet becomes
Figure 3 is the lifecycle of an Android application, termed an "Activity." The diagram reflects the more powerful capabilities of the hardware platform. Nowadays, high-end phones often have 1 GB of RAM and up to 16 GB of Flash memory. They have larger color screens (240×320, which are now more common), along with faster processors and features like cameras and GPS. The extra states in an Activity's lifecycle not only address some limitations in the MIDP specification, but also enable support for the concurrent execution of Activities.
An Activity's lifecycle starts with a call to
onCreate(), which handles set-up chores and static global initializations. The
onStart() method begins the Activity's visible lifetime, where it presents its UI window on-screen. Invoking
onResume() has the Activity begin interactions with users. The application is in the foreground and active state. If another application must come to the foreground, Android calls the
onFreeze() method to save the Activity's state. To this end, Android provides support methods that let you easily store and retrieve various data elements in objects called "Bundles." After you save the Activity's context,
onPause() is called. The Activity stops any animations, commits its variables to persistent storage, and enters the paused state. At this point, the other Activity's UI window can come to the foreground and interact with users.
What happens to the Activity next depends on several things. If the foreground application only partially obscures the screen, the Activity hovers in the paused state, as users might bring it back to the foreground at any moment. If the foreground application's window completely covers the screen,
onStop() is called and the Activity enters the stopped state. This state is similar to the paused state, except that for low-memory situations, Android might dispose of its window. If the Activity returns to the foreground later, calls to
onStart() restore the window and UI. In very low memory situations, Android might kill the Activity to release memory. If users select the Activity again, it must be completely restarted and its context restored from saved Bundles. As Figure 3 illustrates, the additional methods and states let the Activity cleanly resume execution whether it's been paused, stopped, or destroyed.