S-JIS[2004-11-23/2005-12-31]
iアプリもjavaであることには変わりないので、覚えることは独特のクラスだけ。と思いたい…。
普通のJavaアプリケーションの場合、javaコマンド実行時に指定したクラスのmain関数が最初に実行される。
iアプリの場合、com.nttdocomo.ui.IApplicationを継承したクラスのpublic void 
start()メソッドが最初に実行されるらしい。
(どのクラスを実行するかは別途指定するみたいだけど)
package jp.hishidama.iappli; import com.nttdocomo.ui.IApplication; public class Hello extends IApplication { /* (非 Javadoc) * @see com.nttdocomo.ui.IApplication#start() */ public void start() { // TODO 自動生成されたメソッド・スタブ } }
Eclipseから普通のJavaアプリケーションを実行する方法と同様。
主な違いは、「実行(N)」ダイアログの「構成(F)」ペインで「DoJa-3.0 アプリケーション」を選ぶこと。
でもそのまま実行しても、コンソールに「AppName の値がありません」とエラーが表示されるだけだけど…。
これは、iアプリ独特の「jamファイル」とかいうのが足りない所為らしい。(JAM:Java Application 
Manager)
Eclipseには、jamファイルを編集する為の特別なツールは無い。普通のEclipseのテキストエディタで編集する。
Eclipseのワークスペースの下の「プロジェクト名/bin」の下に、「プロジェクト名と同じ名前.jam」というファイルがある。
最初はパッケージエクスプローラーで見ても見えないかもしれないけど、binを右クリックして「最新表示(F)」すると出てくる。
この中に、必要なものを書いてやる。(並び順は勝手に変更されるので、どこに書いてもOK)
LastModified = Tue, 23 Nov 2004 13:37:53 AppClass = jp.hishidama.iappli.Hello AppName = ひしだまiアプリtest PackageURL = iTest.jar AppSize = 723
| 名前 | 説明? | 例 | 
|---|---|---|
| AppClass | 実行するクラス。パッケージがある場合はそれも書かないと、ClassNotFoundException。 | jp.hishidama.iappli.Hello | 
| AppName | アプリケーションの名前。携帯にダウンロードした際、iアプリのメニューに表示される。 | ひしだまiアプリtest | 
| LastModified | jarファイルの最終更新日時らしい。Eclipseから実行すると、勝手に変わる。 | |
| PackageURL | プログラムが格納されたjarファイルっぽい。勝手に書かれている。jarファイルのファイル名はEclipseのプロジェクト名になる模様。このjarファイル本体は、「プロジェクト名/bin」の下に作られる。 | iTest.jar | 
| AppSize | どうも、PackageURLで指定しているjarファイルのサイズ[BYTE]っぽい。勝手に変わる。 | |
| UseNetwork | ネットワークを使用する際に指定が必要。[2005-12-03] | UseNetwork = http | 
| SPsize | 使用するスクラッチパッドのサイズ[BYTE]を指定。スクラッチパッドを使う場合は必須っぽい。[2005-12-31] | SPsize = 10240 | 
import com.nttdocomo.ui.*;
〜
    public void start() {
        System.out.println("start");
        Panel panel=new Panel();
        panel.add(new Label("Hello,world"));
        Display.setCurrent(panel);
        System.out.println("end");
    }
パネルのインスタンスを作り、文字列を表示するためのラベルを作ってパネルに入れ、最後にそのパネルを表示しているという感じだろうか。
System.out.println()の出力先は、Eclipseのコンソール。
なお、例外発生時のprintStackTrace()では、普通のjavaと違ってソースの位置や呼び出し履歴は出力されなかった…すこぶる不便。
public class Hello extends IApplication {
    /* (非 Javadoc)
     * @see com.nttdocomo.ui.IApplication#start()
     */
    public void start() {
        System.out.println("start");
        Frame frm=new HelloCanvas();
        System.out.println("display");
        Display.setCurrent(frm);
        System.out.println("end");
    }
}
class HelloCanvas extends Canvas {
    /* (非 Javadoc)
     * @see com.nttdocomo.ui.Canvas#paint(com.nttdocomo.ui.Graphics)
     */
    public void paint(Graphics g) {
        System.out.println("paint_start");
        g.lock();
        g.setColor(Graphics.getColorOfName(Graphics.BLUE));
        g.drawString("Hello,canvas",16,32);
        g.unlock(true);
        System.out.println("paint_end");
    }
}
ちなみに、コンソールへのログの出力順は、ちょっと不思議。start()の中で処理が完結しているわけではないみたい。きっと、paint()は実際に画面に表示されるときに呼ばれるんだろうな。(VC++でOnDraw(CDC *pDC)が呼ばれるのと同じようなもの?)
start display end paint_start paint_end
Display#setCurrent()に渡すフレームを変えてやれば切り替えられるみたい。
IApplicationの派生クラス:
public class Hello extends IApplication {
    /* (非 Javadoc)
     * @see com.nttdocomo.ui.IApplication#start()
     */
    public void start() {
        Display.setCurrent(new HelloPanel()); //最初はパネルを表示
    }
}
パネルの派生クラス:
public class HelloPanel extends Panel implements SoftKeyListener { /** * コンストラクター */ public HelloPanel() { //タイトル setTitle("パネルのテスト"); //ソフトキーの初期化 setSoftLabel(SOFT_KEY_1, "CHG"); setSoftLabel(SOFT_KEY_2, "QUIT"); setSoftKeyListener(this); //ソフトキーの処理に自分(SoftKeyListenerの派生クラス)を登録 //レイアウトマネージャーを無効化 setLayoutManager(null); //ラベルを付けてみる Label label = new Label("パネルのラベル"); label.setForeground(Graphics.getColorOfName(Graphics.BLUE)); label.setLocation(32, 48); //←レイアウトマネージャーを無効にしていないと無視される super.add(label); } /* (非 Javadoc) * @see com.nttdocomo.ui.SoftKeyListener#softKeyPressed(int) */ public void softKeyPressed(int softKey) { switch (softKey) { case SOFT_KEY_1 : Display.setCurrent(new HelloCanvas()); //キャンバスへ切り替え break; case SOFT_KEY_2 : IApplication.getCurrentApp().terminate(); //アプリケーション終了 break; } } /* (非 Javadoc) * @see com.nttdocomo.ui.SoftKeyListener#softKeyReleased(int) */ public void softKeyReleased(int softKey) { } }
キャンバスの派生クラス:
public class HelloCanvas extends Canvas {
    /**
     * コンストラクター
     */
    public HelloCanvas() {
        //ソフトキーの初期化
        setSoftLabel(Frame.SOFT_KEY_1, "CHG");
        setSoftLabel(Frame.SOFT_KEY_2, "QUIT");
    }
    /* (非 Javadoc)
     * @see com.nttdocomo.ui.Canvas#paint(com.nttdocomo.ui.Graphics)
     */
    public void paint(Graphics g) {
        g.setColor(Graphics.getColorOfName(Graphics.AQUA));
        g.drawString("キャンバスの文字列", 16, 16);
    }
    /* (非 Javadoc)
     * @see com.nttdocomo.ui.Frame#processEvent(int, int)
     */
    public void processEvent(int type, int param) {
        if (type == Display.KEY_PRESSED_EVENT) {
            switch (param) {
                case Display.KEY_SOFT1 :
                    Display.setCurrent(new HelloPanel()); //パネルへ切り替え
                    return;
                case Display.KEY_SOFT2 :
                    IApplication.getCurrentApp().terminate(); //アプリケーション終了
                    return;
            }
        }
        super.processEvent(type, param);
    }
}
public class MoveCanvas extends Canvas implements Runnable { private int x, y; private int k, zk; private java.util.Random rnd = new java.util.Random(); /** * コンストラクター */ public MoveCanvas() { x = 0; y = getHeight() / 2; k = 0; //背景色を指定 setBackground(Graphics.getColorOfName(Graphics.BLACK)); //自分自身のスレッドを作成 Thread runner = new Thread(this); //java.lang.Thread runner.start(); //スレッド実行開始 } /* (非 Javadoc) * @see com.nttdocomo.ui.Canvas#paint(com.nttdocomo.ui.Graphics) */ public void paint(Graphics g) { g.lock(); g.clearRect(0, 0, getWidth(), getHeight()); final int r = 24; g.setColor(Graphics.getColorOfName(Graphics.YELLOW)); g.fillArc(x - r / 4, y - r / 4, r, r, k, 360 - k * 2); //g.fillRect(x - r / 2, y - r / 2, r, r); g.unlock(true); } /* (非 Javadoc) * @see java.lang.Runnable#run() */ public void run() { while (true) { try { Thread.sleep(10); } catch (InterruptedException e) { } //ドットを動かす if (++x >= getWidth()) { x = 0; } if ((rnd.nextInt() & 1) == 0) { if (--y < 0) { y = getHeight() - 1; } } else { if (++y >= getHeight()) { y = 0; } } //角度を変更する if (k <= 4) { zk = 2; } else if (k >= 45) { zk = -4; } k += zk; //再描画を指示 super.repaint(); //キー判定 int key = super.getKeypadState(); if ((key & (1 << Display.KEY_SELECT)) != 0) { IApplication.getCurrentApp().terminate(); //アプリケーションの終了 } } } }
画像は、gif形式を扱える。プロジェクト配下の「res」ディレクトリに置いておけば、ビルド時にjarファイル内にコピーされる。そこからアプリ実行時に読み込み可能。
    private Image loadImage(String name) {
        MediaImage mi = MediaManager.getImage("resource:///" + name);
        try {
            mi.use();
        } catch (ConnectionException e) {
            e.printStackTrace();
            System.err.println("conn:" + name);
        } catch (UIException e) {
            e.printStackTrace();
            System.err.println("ui:" + name);
        }
        return mi.getImage();
    }
このメソッドでは、引数nameにファイル名を渡す。
もし「res/img/hoge.gif」というディレクトリで画像を置いていれば、nameには「img/hoge.gif」を渡せばよい。
単なるデータは、以下のようにして読み込む。[2005-12-03]
    public byte[] loadData(String name, long offset, int len){
        try {
            InputStream is = Connector.openInputStream("resource:///" + name);
            is.skip(offset);
            byte[] buf = new byte[len];
            is.read(buf);
            is.close();
            return buf;
        } catch (IOException e) {
            e.printStackTrace();
            System.err.println("io:" + name);
        }
    }
このメソッドでは、引数nameにファイル名、offsetとlenには読み込む位置と長さを指定する。
もし「res/data.dat」というファイルを先頭から256バイト読みたければ、「byte[] data = loadData("data.dat", 0, 
256);」となる。
ネットワーク経由でhttpでデータを取得する場合、以下のようにして読み込む。[2005-12-03]
    public byte[] getHttpData(String name) {
        String url = this.getSourceURL() + name;
        try {
            HttpConnection conn = (HttpConnection)Connector.open(url, Connector.READ, true);
            conn.setRequestMethod(HttpConnection.GET);
            conn.connect();
            InputStream is = conn.openInputStream();
            byte[] buf = new byte[(int)conn.getLength()];
            is.read(buf);
            is.close();
            conn.close();
            return buf;
        } catch (Exception e) {
            Dialog d = new Dialog(Dialog.DIALOG_ERROR, "サーバーファイル取得エラー");
            d.setText(url + "\n" + e.toString());
            d.show();
            return null;
        }
    }
IApplication#getSourceURL()を使うと、アプリをダウンロードした場所のURLが取得できる。
つまりダウンロード用のjarファイルと同じ場所にデータファイルを置いておけば、上記の方法でファイルを読み込める。
(ただし、Eclipseからエミュレータで動かす場合は「/」しか返ってこないのでurlが正しくならず、エラーになるが…)
それから、ネットワークを使用する際にはjamファイルに「httpを使用する」という指定をする必要がある。
UseNetwork = http
この指定が無いと、「UseNetworkというキーがADF(jamファイルのこと)に無い」という例外が発生する。
java.lang.SecurityException: UseNetwork key not found in ADF
この指定を入れると、実機でアプリケーションを動かす際に通信するかどうか(ネットワークを使用するかどうか)を聞いてくる。
ここで「使用する」を選択すれば通信できるが、「使用しない」を選択すると以下のような例外が発生し、通信できない。
java.lang.SecurityException: User selected not use network
アプリケーションが終了しても保持しておきたいデータは、スクラッチパッドに保存して読み込める。[2005-12-31]
アプリケーションのjarサイズにも制限があるので、固定データを 初回はhttpから取得してスクラッチパッドに書き込み、次回以降はスクラッチパッドから読み込む…という事もできるんだけど、アリかなぁ?
    /** スクラッチパッドから画像を取得する */
    public Image getScratchImage(int pos) {
        try {
            MediaImage mi = MediaManager.getImage("scratchpad:///0;pos=" + pos);
            mi.use();
            return mi.getImage();
        } catch(Exception e) {
            Dialog d = new Dialog(Dialog.DIALOG_ERROR, "スクラッチイメージ取得エラー");
            d.setText(pos + "\n" + e.toString());
            d.show();
            return null;
        }
    }
    /** スクラッチパッドへデータを保存する */
    public void saveScratchData(int pos, byte[] data) {
        try {
            OutputStream os = Connector.openOutputStream("scratchpad:///0;pos=" + pos);
            os.write(data);
            os.close();
        } catch(Exception e) {
            Dialog d = new Dialog(Dialog.DIALOG_ERROR, "スクラッチ保存エラー");
            d.setText(pos + "\n" + e.toString());
            d.show();
        }
    }
読み込みでも書き込みでも、「scratchpad:///0;pos=0」といった文字列を指定することでアクセスできる。
スクラッチパッドを使用する際にはjamファイルに「スクラッチパッドサイズ」を指定する必要がある。
SPsize = 10240
この指定が無いと、「レングス不正」という例外が発生する。
java.io.Exception: Illegal length