今回は、前回作成したソースコードを解析しながら、iアプリのソースコードの概略を理解していくことにしましょう。――前回のコードを整理してよく見ると、以下のような形になっていることがわかるかと思います。
import com.nttdocomo.ui.*;
public class Test1 extends IApplication {
・・・クラスの定義・・・
}
class Test1Frame extends Panel implements SoftKeyListener {
・・・クラスの定義・・・
}
まず、import文で使用するライブラリを宣言し、その後に「Test1」と「Test1Frame」という2つのクラス定義が記述されていることがわかります。iアプリは、このように2つのクラスで1つになっているものが多いのです。
1つめのTest1は、「IApplication」というものをextendsして作られていますね? これは、com.nttdocomoというi-mode用パッケージにあるクラスで、iアプリの土台ともなるべきものです。これが、iアプリのアプリケーションなのです。iアプリは、全てこのIApplicationクラスのサブクラスでなければいけません。
では、2つ目の「Test1Frame」は何かというと、これが実際に画面に表示されているものなのです。これはPanelを継承しています。といっても、皆さん御存じのjava.awtにあるPanelではありません。これも、com.nttdocomoに用意されているクラスなのです。基本的にはawtのPanelとほぼ同じで、四角い領域となるコンテナクラスです。IApplication自体は、FrameやPanelなどのようにGUIとして表示する機能を持ったものではありません。ですから、それとは別に、実際に表示するGUIを持ったクラスが必要となります。
このようにiアプリでは、まずIApplicationを継承したアプリケーションクラスをロードし、そこから実際に表示するクラスを組み込んで表示する、という形になっているのですね。
では、全体の構成がわかったら、順に説明をしていきましょう。まず最初のimportですが、ここでは「com.nttdocomo.ui」というパッケージをimportしていますね? これは、ユーザーインターフェイス関係のクラスがまとめられたパッケージです。NTT-DoCoMoがi-mode用に用意してあるライブラリは、全てcom.nttdocomoというところにまとめられています。このuiパッケージの他にもパッケージはいくつかありますが、まずはこれをある程度使えるようになりましょう。それだけでも、かなりいろいろ作れるようになります。
次にクラスの定義へと進みます。1つ目は、IApplicationを継承したTest1クラスでしたね。これは、以下のような形をしていました。
public class Test1 extends IApplication {
public void start() { Display.setCurrent(new Test1Frame(this)); }
}
このTest1クラスには、startというメソッドが1つだけあります。i-modeでは、IApplicationがロードされると、最初にこのstartメソッドを実行します。これは、Appletで最初にinit,startといったメソッドを実行するのと同じ感覚で考えればよいでしょう。
ここで実行しているのは、「Display.setCurrent」というメソッドです。最初の「Display」はデバイスの表示を抽象化したクラスで、端末の液晶スクリーンとキーパッドを操作したり、これらの情報を取得するのに用います。要するに「このDisplayが実際の端末を示すオブジェクトなのだ」と考えればいいでしょう。このDisplayクラスに用意されているメソッド類は「クラスメソッド」といって、インスタンスを作らずクラスから直接呼び出すことができます。従って、「Display d = new Display()」などというようにDisplayのインスタンスを作る必要はありません。
そして、setCurrentは、カレントフレームを設定するものです。i-modeでは、表示するクラスは「フレーム」と呼ばれ、現在選択されているフレームが、カレントフレームです。このsetCurrentで表示するクラスのインスタンスをカレントフレームに設定することで、そのインスタンスが画面に表示されます。
ここではパラメータに「new Test1Frame」としてTest1Frameのインスタンスを設定しています。が、よく見ると、thisをnew Test1Frameの引数にしていますね? これは、実は重要です。
見ればわかるように、IApplicationのサブクラスであるTest1クラスと、実際に表示するTest1Frameクラスは、それぞれ独立したクラスです。従って、Test1Frame側からは、Test1クラスは見えないのです。そこで、パラメータでthisを渡して変数などに保存しておき、Test1を利用する必要がある場合にはそれを用いるようにしよう、というわけです。
次に、Test1Frameクラスへと進みましょう。こちらは、ちょっとだけややこしくなっています。それというのも、キーパッドをプレスした時のイベント処理なんかを組み込んでいるからです。
まず、継承しているPanelクラスですが、これはawtのPanelと基本的には同様のものをイメージしていいでしょう。addでコンポーネントを組み込んだり、paintメソッドでGraphicsインスタンスを利用して画面描画を行なったり、だいたい似たような感覚で行えます。
com.nttdocomo.uiには、この他にCanvasというクラスもあり、これもawtのCanvasと同様の感覚で使えます。iアプリで実際に画面に表示するためのクラスとしては、だいたいこの2つのいずれかを用います。コンポーネントを組み込んで使う場合にはPanelを、直接グラフィックを描画して使う場合はCanvasを使うのが一般的のようです。
このTest1Frameクラスでは、この他にちょっと仕掛けがしてあります。クラス定義をよく見ると、
class Test1Frame extends Panel implements SoftKeyListener {
――という形になっていますね?「SoftKayListener」というものがimplementsされています。これは、キーパッドの右上と左上にあるボタンのイベントを扱うためのイベントリスナーなのです。
この左上と右上の2つのボタンは、スクリーン下部に表示されるラベルの機能を実行するのに用いられるもので、「ソフトキー」と呼ばれるものです。このソフトキーは、i-modeでは他のキーパッドと別に分けて扱われるようになっています。このソフトキーを操作した際のイベントを処理するためのものがSoftKayListenerというわけです。別のクラスとしてリスナーを定義してもいいのですが、ここでは面倒なのでこのTest1Frameクラス自身に持たせることにしました。
クラス定義の最初の部分には、「IApplication app;」としてIApplicationをおさめるクラスフィールドを用意してあります。その次に、コンストラクタがあります。
public Test1Frame(IApplication a){ app = a; setTitle("Hello"); setSoftLabel(Frame.SOFT_KEY_1, "Exit"); setSoftLabel(Frame.SOFT_KEY_2, "TEST"); setSoftKeyListener(this); }
まず、appにパラメータで渡されたIApplicationを設定します。これで、IApplicationを呼び出す必要があれば、appを使って利用できるようになりますね。
次に、「setTitle」でこのフレームのタイトルを設定しています。Panelにはタイトル表示の機能があり、このようにしてタイトルを設定すると、一番上にそれが表示されます。
その次にある「setSoftLabel」は、ソフトキーのラベルを設定するものです。パラメータは2つあり、1つ目が設定するソフトキー、2つ目が表示するテキストとなります。1つ目のパラメータは、Frameクラスに用意されている値を使い、「Frame.SOFT_KEY_1」「Frame.SOFT_KEY_2」というように設定します。
このsetSoftLabelで注意しておきたいのは、「半角文字4文字、全角なら2文字しか表示されない」ということです。それ以上を設定してもエラーにはなりませんが、画面には表示されません。
そして最後に「setSoftKeyListener」でSoftKeyListenerというリスナークラスを設定します。「add○○Listener」というメソッドに慣れているとsetで始まるメソッド名は奇妙に感じますが、i-modeのイベントリスナーは基本的に複数設定できないのです。それで「add」ではなく「set」になっているんでしょうね。
このSoftKeyListenerは、「softKeyPressed」「softKeyReleased」という2つのメソッドを持っています。従って、このSoftKeyListenerをimplementsしたクラスでは、必ずこの2つのメソッドを用意しなければいけません。今回は、プレスした時のみのイベントを用いることにしましたので、softKeyReleasedはただの空っぽのメソッドになっています。
public void softKeyPressed(int softKey) { switch(softKey){ case Frame.SOFT_KEY_1: app.terminate(); break; case Frame.SOFT_KEY_2: Dialog dl = new Dialog(Dialog.DIALOG_INFO,"Test"); dl.setText("This is Test."); dl.show(); break; } }
これが、ソフトキーのパッドを押した時に呼び出されるメソッドです。引数には、イベントが発生したソフトキーを示す値が収められています。
ここでは、switch文を使って、押したキーによって処理を行なうようにしてあります。まず、左側のソフトキーが押された場合(case Frame.SOFT_KEY_1:)には、「app.terminate()」というものを実行していますね。このterminate()が、iアプリを終了するメソッドです。iアプリでは「System.exit」で終了してはいけません。これで終了しようとすると例外が発生してしまいますので注意して下さい。
ここで、「なぜIApplicationをわざわざパラメータで渡したのか」がようやくわかったことでしょう。iアプリを終了するには、起動しているIApplicationインスタンスのterminateを呼び出す必要があったのです。それで、わざわざインスタンスを引き渡していたのですね。
さて、もう1つのソフトキーを押した場合(case Frame.SOFT_KEY_2:)を見てみましょう。こちらは、「Dialog」クラスのインスタンスを作成していますね。これも、やっぱりawtのDialogではなく、com.nttdocomoのクラスで、簡易メッセージを表示するためのものです。
このDialogには様々な機能やオプションがあるのですが、ここでは2つのパラメータを指定した形でインスタンスを作成しています。1つ目はメッセージダイアログのタイプを指定するもので、Dialogクラスの値を使って指定します。「Dialog.DIALOG_INFO」は、インフォメーションダイアログというもので、OKするボタン1つだけのあるメッセージダイアログです。2つ目のパラメータには、タイトルとなるStringを指定します。
そして、setTextメソッドで、実際に表示するテキストを指定します。以上の準備が整ったら、最後にshowでメッセージダイアログを表示します。このDialogはモーダルになっていて、ダイアログが消えるまで処理は停止しています。
このDialogは、私のところではなんかちょっと変なところがあって、実際にいろいろ試したところ、予想した動作が得られない現象がたまにありました。が、これはi-JADE Liteの問題なのか、実際に503iで同種の問題があるのかわかりません。そのへん、Dialogクラスの動作について情報をお持ちの方がいたら教えて下さいね。
というわけで、これでなんとかiアプリのソースコードの基本的な流れがわかりました。お疲れさま! 一応、これでソフトキーとメッセージダイアログの使い方の基礎はわかりましたから、自分でちょこちょこアレンジして遊んでみるといいですよ!
これで一応プログラムはできましたが、このままではちょっと問題があります。まず、クラスファイルの状態のままではどうもスッキリしません。iアプリは、必要なファイル類をJARファイルとしてまとめて利用するのが一般的なようですので、まずJavaアーカイブの作成が必要です。
まあ、これは簡単な問題ですが、もっと重要な問題も残っています。実際にはiアプリは非常に限られた環境下で実行されますから、ダウンロードして実行しようとしたらメモリが足りなくて動かなかった、なんてことも起こりかねません。なにしろ、503iで利用可能なiアプリのアプリケーションサイズは最大で10K(10240バイト)以下でなければいけないのです。
こうした問題を解決するために、i-modeには「Java Application Manager」というものが用意されています。これはiアプリの管理を行なうためのもので、専用の形式で記述されたファイルを用意することで、問題なくiアプリのロードが行えるようになっています。このJAMのためのファイルを一般に「JAMファイル」と呼びます。他に「ADF」とも呼ばれ、NTT-DoCoMoの技術文書ではこちらを採用しています。
JAMファイルは、拡張子「jam」をつけたシフトJISコードのテキストファイルです。これには、「キー」と呼ばれる様々な項目とその値を記述しておきます。JAMファイルで利用できるキーにはいろいろありますが、必須となっているのは以下のものです。
AppName = 《iアプリ名》 PackageURL = 《パッケージのURL》 AppSize = 《アプリケーションサイズ(バイト単位)》 AppClass = 《IApplicationクラス名》 KvmVer = 《KVMのバージョン》 LastModified = 《最終修正日時》
いくつか補足しておきましょう。まずpackageURLですが、これはパッケージファイルの場所/名前を指定するものです。単純に同一階層にまとめた場合は、JARファイル名を指定します。
KvmVerは、Kvmのバージョンですが、これは現状では「CLDC-1.0」とするようです。これが、現在の503iで採用されている正式なバージョンです。ただ、実際には単に「1.0」と指定されることが多いようで、これでも大丈夫のようです。またLastModifiredは、「曜日 日 月 年 時 分 秒」の順番に、英語の日付表記形式で記述します。曜日と月はアルファベット3文字の短縮形、年は西暦の4桁表記となります。
一応、DoCoMoの仕様書に「必須」と書かれているのはこれだけなのですが、この他に「UseNetwork」というキーも覚えときましょう。これは利用するネットワークを指定するもので、データをサーバからダウンロードしたりする場合は、「UseNetwork=http」というのを追加しておく必要があります。これがないと、503iのiアプリにある「ネットワーク接続」の設定が利用できない状態になるはずです。
また、「AppVer」というキーもつけるのが一般的のようです。これはiアプリのバージョンを示すもので、要するにバージョンアップなどの場合を考えて用意されているのでしょう。まあ、これは最初のものは「AppVer=1.0」として、バージョンアップするごとに値を変えていく、ということでよいと思います。
では、先に作成した「Test1」iアプリを実際に動かせるようにしてみましょう。――まず、JARファイルを作成します。MS-DOSプロンプトを開き、cdでクラスファイルのあるディレクトリに移動して、以下のように実行します。
jar cvf Test1.jar Test1.class Test1Frame.class
JARコマンドについては他の回で説明してますので省略します(「JavaBeansの作成」「Excutable Java Archive」を参照)。これで、2つのクラスをまとめた「Test1.jar」というJavaアーカイブファイルが作成できます。次に、テキストエディタを起動し、以下のようにテキストを記述します。
AppName = Test1 AppVer = 1.0 PackageURL = Test1.jar AppSize = 1428 AppClass = Test1 KvmVer = CLDC-1.0 LastModified = Sun, 01 Jan 2001 23:16:54
これが、JAMファイルの中身です。記述したら、「Test1.jam」というファイル名でJavaアーカイブファイルと同じディレクトリにシフトJISのキャラクタコードで保存します(通常のWindows 98系のテキストエディタはシフトJISで保存します)。LastModifiedなどは、上を参考にそれぞれ自分の作成したTest1.jarの最終変更日を記して下さい。
できあがったら、i-JADEを起動し、iアプリとして「Test1.jam」を指定して実行してみて下さい。ちゃんとiアプリがロードされ実行できるでしょう? これで、iアプリは本当に完成です。
…といいたいところですが(またこれか)、プログラム自体はこれで確かに完成で、エミュレータなどで動作確認できるのですが、こうしてできたJARが実際に503iで動くかというと、おそらくうまくいかないようです。なぜかというと、現在発売されている503iでは、「事前検証」という処理が行なわれていないJavaのプログラムは実行できないようになっているらしいからです。
この「事前検証」というのは、文字通りクラスファイルを実行前に検証する作業のこと。Java2 Micro Editionはかなり制約された環境で動作するものですから、こうしたものは事前検証を行なって実行時の負荷を軽減させる必要がある、ということのようです。
この事前検証のためには「preverify」というプログラムが必要です。これは、Java2 Micro Edition CLDCというものの中に入っているので、これをSun Microsystemsのサイトからダウンロードしないといけません。このダウンロードは、デベロッパーとして登録をすると利用できるようになっています。といっても、別に料金がかかるとかそういうことではなくて、住所氏名やアクセスのID/パスワードを書けば誰でもタダで登録できます。Java関係は現在どんどん新しい技術が出て来ているまっただ中ですから、登録して各種の開発版などを利用できるようにしておくと非常に勉強になりますよ。
で、「J2ME_CLDC」というものをダウンロードして解凍すると、実行ファイルが入っている「bin」フォルダの中に「preverify.exe」というものが見つかるはずです。これが事前検証プログラムです。これもjavacなどと同様にMS-DOSプロンプトから実行します。
ただし、そのためにはCLDCの他に、NTT-DoCoMoのライブラリがなければいけません。これは、まだDoCoMoが正式に出してないんですが、手許にあるi-JADEに含まれていますから、これを使えばよいでしょう。
jar xvf i-jade-p.jar
i-JADEのディレクトリに移動し、このように実行すると、i-jade-p.jarが全て解凍されます。そうしたら、「classes」というフォルダの中にある「ntt」というフォルダを探してください(「com」フォルダの中にあるはずです)。これが、NTT-DoCoMo独自のクラスライブラリです。見つかったら、J2ME_CLDCの「bin」フォルダ内にある「classes」フォルダの「com」フォルダ内に収めます。要するに、CLDCのクラスライブラリの中に「ntt」を追加するわけですね。
そしてできたら、作成したプログラムの事前検証を行ないます。
c:\j2me_cldc\bin\preverify -classpath c:\j2me_cldc\bin\classes Test1Frame
例えば、CLDCのクラスライブラリのある場所が「c:\j2me_cldc\bin\classes」だとすると、Test1.Frame.classを検証するのは上のようになります。Test1.classのあるディレクトリに移動して上記を実行すれば、検証が行なわれ、「output」というフォルダの中に、検証されたクラスファイルが作成されるはずです。同様にしてTest1.classも検証をしましょう。
…まあ、上の書き方だと、pathやclasspathがわやくちゃになってわかりにくいことこの上ないですから、ここらで環境変数としてきちんとpathとclasspathを設定しておいた方がいいでしょう。毎回、フルパスを指定するのは面倒ですからね。
こうして作られたクラスファイルは、事前検証済みであり、ちゃんと503iで実行できるはずです。後はこれをJARにまとめて使えばいいわけです。その際、JAMファイルの修正日とファイルサイズを確認して変更するのを忘れないように。まあ、エミュレータでは事前検証はなくとも動きますから、とりあえず開発時はそのままJAR化して動作チェックして、「これで完成!」となったら最後にpreverifyしてJARを作成する、というように考えるといいでしょう。
※追加情報
現在、NTT-DoCoMoより、純正のiアプリ開発環境がリリースになっています。「J2ME Wireless Toolkit for DoJa」というもので、プログラムのコンパイル、プリベリファイ、JARへのアーカイブ化、JAMファイルの作成を全て一括して行なってくれます。またエミュレータも内蔵しており、その場で動作確認もできます。これを利用すれば、プリベリファイやJAMファイルの作成など面倒な作業を全てやってくれるので、大変便利です。
ただし、エミュレータは特定の機種をエミュレートするのではなく、「仮想のDoJa標準機」といったものなので、エミュレーションチェックは、やはりi-JADE
Liteがいいように思えます。プログラムのコンパイル作業はDoCoMo純正で行ない、できたらi-JADEでチェックする、とういのがベストですね。
最後に、「このiアプリをi-modeでダウンロードするためのHTMLタグ」についても触れておきましょう。iアプリのダウンロードには、<object>タグと<a>タグの組み合わせで行なうのが基本のようです。<object>タグでJAMファイルのURLなどを指定し、<a>タグで、それを指定したリンクを用意します。例えば、こんな感じです。
<object declare id="《オブジェクト名の指定》"
data="《jamファイルのURL》"
type="application/x-jam">
Test1アプリ
</object>
<a ijam="#《idの値》" href="《iアプリ未対応時のHTML》">
ダウンロード
</a>
<a>タグでは、ijamオプションで<object>タグと、hrefで普通のHTMLと、両方を用意します。こうすることで、iアプリに対応していないi-modeでは自動的にhrefのリンクへと移動し、Java対応i-modeではijamのリンクへと移動するようになります。 そして、実際のダウンロードは、<object>タグでdataオプションにJAMファイルを、typeにapplication/x-jamをそれぞれ指定しておくことで行なわれるようになります。