Softbank JSCL-1.2.2搭載端末向けのJava実行環境では、MIDletスイートの個々のMIDletを選択して実行させることの可能な「MIDletセレクタ」を実装しています。
MIDletスイートを作成するには、複数のMIDletをJARファイルにパッケージして、JAD ファイル内にMIDlet属性「 MIDlet-<n>」(n は1から始まる連番)でそれぞれの MIDlet を指定します。
ここでは、MIDletセレクタの振る舞いについて説明します。
JAMからJava実行環境が起動されたとき、実行を要求されたJavaアプリケーションがMIDletスイートであった場合、Java実行環境はMIDlet セレクタを表示し、MIDletスイート中のMIDletをリスト表示してそのうちのひとつをユーザーに選択、実行させます。
図1にMIDlet セレクタの画面例を示します。
図1. MIDletセレクタ画面例
図1は「UIDemo」「UIDemo2」の2つのMIDletが指定されている場合の例です。 図中の「MIDletセレクタを終了するコマンド」は機器メーカーごとにカスタマイズ可能なため、「戻る」以外の文字列が表示される場合があります。
MIDlet セレクタの振る舞いを説明するために、機器で実行されるプログラムを概略下記の3つに分けます。
機器はこれらのプログラムのどれかの画面を表示しています。これらのプログラムの表示状態の遷移を図2に示します。
図2. MIDletスイート実行状態の遷移
ネイティブアプリケーション(JAM)はユーザーの操作に基いてVMを起動し、ユーザーが選択したJavaアプリケーションの実行を要求します。Java実行環境は、実行を要求されたプログラムがMIDletスイートであった場合MIDletセレクタを表示します。
MIDletセレクタは、JADファイルに記述されているMIDlet-<n>に基きMIDletスイートの個々のMIDletをユーザーにリスト表示し、選択実行させます。選択されたMIDletはstartApp()が呼び出され、実行状態となります。
MIDletセレクタ画面に戻り、他のMIDletの実行の機会をユーザーに与えるには、実行中のMIDletが自らnotifyPaused()、またはnotifyDestroyed()を呼び出して、それぞれ一時停止かまたは終了することをJava実行環境に通知するか、DisplayクラスのsetCurrent()メソッドにnullオブジェクトを渡すことで画面表示することを放棄します。
MIDletセレクタは、一時停止中のMIDletや表示を放棄しているMIDletの有無にかかわらず、MIDletスイート中のすべてのMIDletをリスト表示し、MIDletの実行、または再開のための操作をユーザーに許します。MIDletセレクタは、ユーザーから実行を要求されたMIDletのstartApp()を呼び出します。この時点でstartApp()が呼び出されたMIDletが表示されることになります。表示を放棄しているMIDletが存在した状態で、別のMIDletを選択した場合は、複数のMIDletが同時に実行されることになります。
機器側からJava実行環境に一時停止の要請があった場合、VMが一時停止され、機器のネイティブプログラムが実行されます。VMの一時停止の前にForegroundになっているMIDletが存在すれば、そのMIDletのpauseApp()が呼び出されます。
機器側がJava実行環境に再開を要請すると、VMは再開され、一時停止前の表示状態に戻ります。一時停止前にMIDletセレクタが表示されていたときは、MIDletセレクタが表示されるのみです。一時停止前にMIDletが表示されていたときは、表示されていたMIDletのstartApp()が呼び出されて再開されます。
すべての状態において、機器側からJava実行環境に終了の要請があったときは、すべてのMIDletに対してdestroyApp()が呼び出されMIDletは終了させられます。そしてVMが終了し、ネイティブアプリケーションの実行に移ります。VMが終了するため、再度Javaアプリケーションを実行するには、上記1のステップから始めなくてはなりません。
MIDletスイートではなく、ただひとつのMIDletで構成されるJavaアプリケーションの場合、MIDletがnotifyPaused()、またはnotifyDestroyed() を呼び出すとVMは一時停止するか終了します。この場合、MIDlet側で生成した資源について他のMIDletへの影響を考慮する必要はありません。一方、上記3の遷移過程においてはMIDletがnotifyPaused()、またはnotifyDestroyed() を呼び出しても、VMは実行されています。資源の解放は自動的には行われません。たとえば、MIDletが別スレッドを起動していた場合にMIDletセレクタがそれらの別スレッドを停止または終了することはありません。したがってnotifyPaused()、またはnotifyDestroyed() を呼び出す前に、MIDlet側で適切に資源の解放を行う必要があります。
単一MIDletのJavaアプリケーションかMIDletスイートかにかかわらず、個々のMIDletにとってのライフサイクルは図3に示すものになります 。ただし、上述したようにnotifyPaused()、またはnotifyDestroyed()において、資源の解放に関する責任がそれぞれで異なります。また、「Paused」と「Destroyed」の状態ではそのMIDletは表示されることはありません。また「Active」な状態とは、必ずしもそのMIDletが表示されていることを意味しません。
図3. MIDletライフサイクル
MIDlet セレクタにより、MIDletスイート中のMIDletを複数起動することが可能です 。ただし、機器の画面への表示とキー入力の受け付けが可能なMIDletはある時点で1つしか存在できません。画面表示とキー入力受け付けの権利を持った状態を Foreground状態、権利を持たない状態をBackground状態と呼びます。Foreground、Backgroundそれぞれの状態におけるMIDletの状態を表1に示します。
項目 | Foreground | Background |
画面表示とキー入力受け付け | 可 | 不可 |
MIDletの「現在のDisplayable」のisShown()メソッドの戻り値 | true | false |
MIDlet セレクタは、ユーザーにMIDletを選択させ、実行します。その結果MIDletのstartApp()が呼び出され、Active状態になります。MIDletはDisplayクラスのsetCurrent()にDisplayableオブジェクト指定することで、画面表示すること(すなわちForegroundになること)をJava実行環境に要求します。この時点でMIDletはForegroundになります。
ForegroundになっているMIDletがnotifyPaused()やnotifyDestroyed()を呼び出し、Active状態でなくなると、別のMIDletまたはMIDletセレクタがForegroundになります。
ForegroundとなっているMIDletはDisplayクラスのsetCurrent()にnullを指定することで画面表示の放棄(すなわちBackgroundになること)をJava実行環境に要求できます。この結果別のMIDletまたはMIDletセレクタが表示される(すなわちForegroundとなる)ことになります。しかし、このBackgroundになったMIDletはまだActive状態のままであり、何らかの処理を継続できます。
MIDletセレクタがForegroundになった場合は、MIDletスイート中の任意のMIDletをユーザーに選択させることができます。表示を放棄したMIDletをユーザーが選択した場合、再度そのMIDletのstartApp()が呼び出されます。ユーザーが別のMIDletを選択した場合、その別のMIDletのstartApp()が実行され、その結果複数のActiveなMIDletが存在する状態となります。
ひとつ以上のActiveなMIDletが存在して、さらにあるMIDletがForegroundとなっている状態においては、どのMIDletをForegroundとするかはMIDletの振る舞いによって決まります。MIDletの以下の行動がMIDletのForeground/Background切り換えの発生要因となります。
ForegroundのMIDletがnotifyPaused()またはnotifyDestroyed()を呼び出しActive状態からPaused状態またはDestroyed状態に遷移する
Paused状態のMIDletがresumeRequest() を実行し、Active状態に遷移する
ForegroundのMIDletがDisplay.setCurrent(null) を実行し表示の放棄 (Backgroundへの移行)を要求する
MIDletが上記のいずれかの行動を起こすと、Java実行環境は次にForegroundとするMIDletを下記の手順で決定します。
Foreground となっている、またはForegroundになることを要求しているMIDletを起動順の逆順に検索します。
上記1.の検索条件に該当するMIDletがForegroundであり、かつresumeRequest()を実行してActive状態に遷移した別のMIDletが存在すれば、その別のMIDletを新たにForegroundとします。そうでなければ、上記1.の検索を続行します。
上記1.の検索条件に該当するMIDletがBackgroundでForegroundになることを要求している場合、そのMIDletをForegroundとします。
上記1.2.3.で該当するMIDletがない場合はMIDletセレクタ画面がForegroundとなります。
Foreground/Backgroundの切換が発生するとき、切換の対象となったMIDletの「現在のDisplayable」がCanvasまたはその派生クラスであれば、そのインスタンスに対して、Foregroundに切り替えられたMIDletではshowNotify(),Backgroundに切り替えられたMIDletではhideNotify()が呼び出されます。
Foregroundの MIDletが CommandMenuList、またはAlertを表示中の期間は上記の切換要因が発生してもForeground/Backgroundの切換は行われません。この期間中にresumeRequest()によってActiveに状態変化したMIDletは、Backgroundのまま startApp()が実行されます。
Paused状態になっているMIDletがresumeRequest()を呼び出したときstartApp()は必ず呼び出されますが、そのMIDletが必ずしもForegroundとならない場合があることに注意してください。上記1.の検索においてForegroundへの移行を要求しているMIDletが先に見つかった場合はそのMIDletがForegroundになります。
MIDletセレクタ画面を表示している間に上記の切換要因が発生してもForeground/Backgroundの切換は行われません。MIDletセレクタ画面が表示されている状態で、 BackgroundとなっているMIDletをForegroundに切り換えるには、ユーザがMIDletセレクタから対象となるMIDletを選択する必要があります。
図4に複数MIDletのForeground/Background切替の例を示します。
図4. 複数MIDletのForeground/Background切替