S-JIS[2004-11-23/2005-12-31]

iアプリのコーディング例

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からの実行方法

Eclipseから普通のJavaアプリケーションを実行する方法と同様。
主な違いは、「実行(N)」ダイアログの「構成(F)」ペインで「DoJa-3.0 アプリケーション」を選ぶこと。

でもそのまま実行しても、コンソールに「AppName の値がありません」とエラーが表示されるだけだけど…。
これは、iアプリ独特の「jamファイル」とかいうのが足りない所為らしい。(JAM:Java Application Manager)

Eclipseでのjamファイルの編集

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と違ってソースの位置や呼び出し履歴は出力されなかった…すこぶる不便。


Canvasを使った例

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

参考


技術メモへ戻る
メールの送信先:ひしだま