1. The MIDlet Lifecycle
The MIDlet lifecycle has been changed in MIDP 3.0. Although the changes can be summarized fairly succinctly there are number of problems with the specification which mean that it is not entirely clear what is supposed to be going on at the detailed level.
To begin with I will describe what the lifecycle now is, given what I take the intent of the changes to be, without getting into too many details and getting bogged down in the problems. I cover those in following section.
1.1 The Changes
MIDlets are no longer paused, cannot pause themselves, and consequently do not need to ask to be resumed. In addition a MIDlet can no longer decline to be destroyed.
As a result the MIDlet methods
notifyPaused()pauseApp()resumeRequest()
are all deprecated, and the pauseApp() method is no longer abstract.
1.2 The Lifecycle of a MIDP 3.0 MIDlet
If we make the not unreasonable assumption that a MIDlet written against the MIDP 3.0 spec will not call any of the deprecated lifecycle methods, and ignoring errors and edge-cases, then the lifecycle of a MIDlet is very simple
- it is constructed via a call to its constructor
- it is then started via a call to its
startApp()method - it then runs until it is destroyed via a call to its
destroyApp()method, or it destroys itself and calls itsnotifyDestroyed()method
1.2 The Lifecycle of a Legacy MIDlet
A legacy MIDlet, that is one written to run under MIDP 1.0 or MIDP 2.0, running under MIDP 3.0, may call notifyPaused() and/or resumeRequest(). This complicates the lifecycle slightly. Ignoring errors and edge-cases as before, then the lifecycle of a legacy MIDlet
in MIDP 3.0 is as follows
- it is constructed via a call to its constructor
- it is then started via a call to its
startApp()method - it then runs
- if while running it calls its
notfifyPaused()method then itsstartApp()method is called immediately
and it continues to run. - if while running it calls its
notifyDestroyed()method then the MIDlet terminates. - if the MIDlet’s
destroyApp()method is called, then on completion, successful or as the result of an exception the MIDlet terminates.
If it calls its notfifyPaused() method other than whilest running the call is ignored.
If at any point it calls its resumeRequest() method the call is ignored.
2. Issues With The Specification Of The MIDlet Lifecycle In The MIDP 3.0 Specification
2.1 The MIDlet Lifecycle State Diagram
Given the apparent simplicity of the MIDlet lifecycle, the state diagram included in the javax.microedition.midlet package documentation looks suprisingly busy. Indeed at first glance it is difficult to see the differences between it and the version in the MIDP 2.1 specification. The differences do become apparent on closer examination but so do the issues.
I am assuming that the diagram is intended to be UML, or UML-like, since the key explicitly mentions UML. On that basis there turn out to be a number of problems. To make it easier to describe them I have converted the diagram into per-state tables, as follows.
Assuming UML, then according to the UML Reference Manual the body of each state should contain
a list of internal actions or activities performed in response to events received while the object is in the state,
without changing state.
These are called internal transitions. An internal transition is of the form
[<event-name>]["(" <arguments> ")"]["[" <guard-condition> "]"]["/" <action-expression>]
A transition to another state is of the form
[<name> ":"][<event-name>][<parameter-list>][<guard-condition>]["/" <action-list>]
For each state in the diagram I have constructed a table of internal transitions and a table of transitions to other states, except
where that turns out not to be possible.
2.1.1 The <Initial> State
Problems
The transition
/ AMS: Create MIDlet() [RuntimeException thrown]
is not legal UML.
Moving the guard to the right place, on the left, gives
[RuntimeException thrown] / AMS: Create MIDlet()
which can be read as saying
if a RuntimeException occurs then the action ‘AMS: Create MIDlet()’ is performed
which is probably not what was intended.
Given the following from the UML Reference Manual
An initial state must have an outgoing completion transition. If there is more than one outgoing transition,
then they must all lack triggers and their guard conditions must partition the possible values.
it is arguable whether the other transition
/ AMS: Create MIDlet()
is legal UML in this context either. It should have a guard.
Assuming this is intended to be the completes normally transition, it can be read as saying
on normal completion the action ‘AMS: Create MIDlet()’ is performed,
which is probably not what was intended either.
2.1.2 The Paused State
Internal Transitions
| ID | <event-name> |
<arguments> |
<guard-conditions> |
<action-expression> |
|---|---|---|---|---|
| Paused: 1 |
AMS: Start |
– | – | – |
| Paused: 2 |
AMS: Destroy |
– | – | destroyApp() |
| Paused: 3 |
MIDlet: notifyDestroyed() |
– | – | – |
| Paused: 4 |
MIDlet: resumeRequest() |
– | Later … | AMS: Start |
Transitions
| ID | <name> |
<event-name> |
<parameter-list> |
<guard-condition> |
<action-list> |
Target State |
|---|---|---|---|---|---|---|
| Paused: 6 | – | AMS: Start | – | – | – | Active |
| Paused: 7 | – | AMS: destroyApp() | – | – | – | Destroyed |
| Paused: 8 | – | AMS: destroyApp() | – | MIDletStateChangeException thrown | – | Destroyed |
| Paused: 9 | – | AMS: destroyApp() | – | RuntimeException thrown | – | Destroyed |
| Paused: 10 | – | MIDlet: notifyDestroyed() | – | – | – | Destroyed |
| Paused: 11 | – | MIDlet: resumeRequest() | – | – | – | Paused |
Problems
Paused: 1
Paused: 1 conflicts with Paused: 6
The ‘AMS: Start’ event results in an internal transition which does nothing, and a transition to the Active state. They cannot both happen.
Paused: 3
Paused: 3 conflicts with Paused: 10.
The ‘MIDlet: notifyDestroyed()’ event results in an internal transition which does nothing, and a transition to the Destroyed state. They cannot both happen.
Paused: 4
Paused: 4 conflicts with Paused: 11
The ‘MIDlet: resumeRequest()’ event results in an internal transition which causes an ‘AMS: Start’ (sic) and a transition back
to the Paused which does nothing. They cannot both happen.
Paused:4 conflicts with the documentation for the method resumeRequest().
Invoking this method MUST have no effect.
Paused: 6
See Paused: 1
Paused:
See Paused: 3
Paused: 11
See Paused: 4
Missing Transitions ?
There is no transition for the ‘MIDlet: notifyPaused()’ event
Possibly this is intentional and denotes that nothing happens, but this is true for other events, but is made explicit, so there
needs to be a consistent approach.
2.1.3 The Active State
Internal Transitions
| ID | <event-name> |
<arguments> |
<guard-conditions> |
<action-expression> |
|---|---|---|---|---|
|
AMS: Start |
– | – | startApp() | |
|
AMS: startApp() |
– | RuntimeException thrown | destroyApp(true) | |
|
Active: 3 |
AMS: Destroy |
– | – | destroyApp() |
|
Active: 4 |
MIDlet: notifyDestroyed() |
– | – | – |
|
Active: 5 |
MIDlet: notifyPaused() |
– | – | startApp() |
|
Active: 6 |
MIDlet: resumeRequest() |
– | – | – |
Transitions
| ID | <name> |
<event-name> |
<parameter-list> |
<guard-condition> |
<action-list> |
Target State |
|---|---|---|---|---|---|---|
|
Active: 7 |
– |
AMS: destroyApp() |
– | – | – | Destroyed |
|
Active: 8 |
– |
AMS: destroyApp(true) |
– |
MIDletStateChangeException thrown |
– | Destroyed |
|
Active: 9 |
– |
AMS: destroyApp(true) |
– |
RuntimeException thrown |
– | Destroyed |
|
Active: 10 |
– |
AMS: startApp() |
– |
RuntimeException thrown |
– | Active |
|
Active: 11 |
– |
MIDlet: notifyDestroyed() |
– | – | – | Destroyed |
|
Active: 12 |
– |
MIDlet: notifyPaused() |
– | – | – | Active |
| – |
MIDlet: resumeRequest() |
– | – | – | Active |
Problems
Active: 1
An ‘AMS: Start’ event results in an internal transition which performs an call to the MIDlet’s startApp() method, although it is unclear what the relationship is between this ‘AMS: Start’ event and the one that resulted in the transition to the Active state.
This behaviour is consistent with the text in Table 15-1: MIDlet states, but they both conflict with the startApp() method documentation whuch states
The method will only be called when the MIDlet is in the Paused state.
although it is arguable that the method documentation itself is either internally inconsistent or very unclear.
Active: 2
Active: 2 conflicts with Active: 10
The completion of an AMS call to the MIDlet’s startApp() method via a RuntimeException results in an internal transition which performs a call to the MIDlet’s destroyApp() method, and in a transition back to the Active state which does nothing. They cannot both happen.
Active: 4
Active: 4 conflicts with Active: 11
The ‘MIDlet: notifyDestroyed()’ events results in an internal transition which does nothing, and a transition to the Destroyed state. They cannot both happen.
Active: 5
Active: 5 conflicts with Active: 12
A ‘MIDlet: notifyPaused()’ event results in an internal transition which performs a call to the MIDlet’s startApp() method, and in a transition back to the Active state which does nothing. They cannot both happen.
Active: 6
Active: 6 conflicts with Active: 13
A ‘MIDlet: resumeRequest()’ event results in an internal transition which does nothing, and in a transition back to the Active state.
One of these is superfluous to requirements.
Active: 10
See Active: 2
Active: 11
See Active: 4
Active: 12
See Active: 5
Active: 13
See Active: 6
Missing Transitions ?
There is no transition for the ‘AMS: startApp()’ event.
Possibly this is intentional and denotes that nothing happens, but this is true for other events, but is made explicit, for example Active: 6. There needs to be a consistent approach.
2.1.4 The Destroyed State
Internal Transitions
| ID | <event-name> |
<arguments> |
<guard-conditions> |
<action-expression> |
|---|---|---|---|---|
|
Destroyed: 1 |
MIDlet: resumeRequest() |
– | – | – |
Transitions
| ID | <name> |
<event-name> |
<parameter-list> |
<guard-condition> |
<action-list> |
Target State |
|---|---|---|---|---|---|---|
|
Destroyed: 2 |
– |
MIDlet: resumeRequest() |
– | – | – | Destroyed |
Problems
Destroyed: 1
Destroyed: 1 conflicts with Destroyed: 2
A ‘MIDlet: resumeRequest()’ event results in an internal transition which does nothing, and a transition back to the Destroyed state which does nothing.
One of these is superfluous to requirements.
Destroyed: 2
See Destroyed: 1.
Missing Transitions
There is no transition for the ‘MIDlet: notifyDestroyed()’ event, or the ‘MIDlet: notifyPaused()’ event, presumably because nothing happens as a result, but then this is true of the ‘MIDlet: resumeRequest()’ event and it has two transitions !
2.2 Life During Construction ?
The construction of a MIDlet is not part of the MIDlet lifecycle. The first state is entered only after the constructor has completed normally or via an exception, yet in its constructor a MIDlet can call any of the lifecycle methods
notifyDestroyed()notifyPaused()resumeRequest()
and some have been known to do just that.
So what should happen ? Possibly nothing, but it would be useful if the specification said so, and it would be easier for it to do so if MIDlet construction was part of the MIDlet lifecycle.
2.3 Backwards Compatibility ?
The current method documentation for notifyPaused() states
Pausing of MIDlets is deprecated. For backward compatibility and to avoid problems with existing MIDlets,
callingnotifyPausedin the Active state MUST causestartAppto be called immediately. The MIDlet
is expecting a call tostartAppto restore it to the Active state.
The previous method documentation for notifyPaused() ends
If the application pauses itself it will need to call
resumeRequestto request to reenter theactivestate.
So arguably a MIDlet implemented on that basis, having called its notifyPaused() method, would not be
expecting a call to
startAppto restore it to the Active state.
at all. In fact it would not be expecting a call to startApp() to occur until after it had made a call to resumeRequest() as per the specification in MIDP 2.
In the circumstances there is probably no right way to ensure backwards compatibility, and there may actually be no legacy MIDlets affected, but it might have been better to say that there is no backwards compatibility in these cases.
3. Revised State Diagram
Below is a revised state diagram. As it is derived from the one in the specification it still suffers from the issues around MIDlet construction as described above, but is hopefully in other respects somewhat clearer than the original.
Events
| Event | Description |
|---|---|
| Start | Synthetic event to trigger transition to Active state |
| Shutdown | Shutdown of MIDlet requested |
| DestroyAppCompletes | The MIDlet’s destroyApp() method completes, either normally or via an exception. |
| StartAppCompletes | The MIDlet’s startApp() method completes normally |
| StartAppException | The MIDlet’s startApp() method completes by throwing an Exception |
| NotifyDestroyed | MIDlet invokes its notifyDestroyed() method |
| NotifyPaused | MIDlet invokes its notifyPaused() method |
Actions
| Action | Description |
|---|---|
| DestroyApp | The MIDlet’s destroyApp() method is invoked with an argument of true. |
| StartApp | The MIDlet’s startApp() method is invoked. |
Guards
| Guard | Description |
|---|---|
| ConstructorCompletes | True if the MIDlet constructor completes normally |
| ConstructorException | True if the MIDlet constructor throws a RuntimeException |
Conventions
If the occurence of an event has no effect when the MIDlet is in that state it is not shown.

Copyright (c) 2009 By Simon Lewis. All Rights Reserved.
[...] There have been changes to existing javax.microedition.midlet.MIDlet methods to support the new MIDlet lifecycle model, and new methods have been added to support new MIDP 3.0 [...]
Pingback by What’s New In MIDP 3.0 ? Part 11: javax.microedition.midlet.MIDlet « Just An Application — June 16, 2009 @ 2:14 pm
[...] What’s New In MIDP 3.0 ? Part 10: The MIDlet Lifecycle [...]
Pingback by “MIDlet not constructed by createMIDlet.” ? « Just An Application — September 11, 2009 @ 11:23 am