{progz}

J2ME alapok: a MIDlet életciklusa, és miért fut le kétszer a startApp

Aki Symbian C++ után nyúl a Javához, először megkönnyebbül. Nincs cleanup stack, nincs kétfázisú konstruálás, és a program a legtöbb mai telefonon fut, nem csak a Series 60-asokon. A J2ME (pontosabban a CLDC konfiguráció és a MIDP profil) programjait MIDletnek hívják. A legfontosabb dolog, amit meg kell érteni róluk, az életciklusuk.

A három állapot

Egy MIDlet három állapotban lehet: szüneteltetett (paused), aktív (active) és megsemmisült (destroyed). Az állapotváltásokat a telefon szoftvere (az AMS, vagyis az alkalmazáskezelő) vezérli, és ilyenkor meghívja a MIDlet egy-egy metódusát:

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class ProgzMidlet extends MIDlet implements CommandListener {
    private Form form;
    private Command exit;
    private boolean started = false;

    public ProgzMidlet() {
        form = new Form("Progz");
        exit = new Command("Kilépés", Command.EXIT, 1);
        form.addCommand(exit);
        form.setCommandListener(this);
    }

    protected void startApp() throws MIDletStateChangeException {
        if (!started) {
            form.append("Szia, J2ME!");
            started = true;
        }
        Display.getDisplay(this).setCurrent(form);
    }

    protected void pauseApp() {
        // itt kell elengedni a szálakat, a hangot, a hálózatot
    }

    protected void destroyApp(boolean unconditional)
            throws MIDletStateChangeException {
        // mentés RMS-be, takarítás
    }

    public void commandAction(Command c, Displayable d) {
        if (c == exit) {
            try { destroyApp(true); } catch (MIDletStateChangeException e) { }
            notifyDestroyed();
        }
    }
}

A kétszer lefutó startApp

A kezdők leggyakoribb hibája, hogy minden előkészítést a startApp-ba tesznek. Indításkor ez működik, de ha közben hívás érkezik, a telefon szünetelteti a programot (pauseApp), utána pedig újra meghívja a startApp-ot. Ilyenkor duplán jelennek meg a lista elemei, két szál indul, vagy elölről kezdődik a játék. A fenti kódban a started jelző véd ez ellen. A felületi elemek pedig eleve a konstruktorban jönnek létre.

Fontos az is, hogy a MIDlet saját magától nem lép ki. A kilépést a notifyDestroyed() jelzi az AMS-nek. A destroyApp csak egy kérés, hogy takarítsunk, ezért a fenti kód kézzel hívja meg kilépés előtt.

A JAD és a JAR

A lefordított és előellenőrzött (preverify) osztályok egy JAR-ba kerülnek, mellé pedig egy JAD leíró tartozik. Sok telefon először a JAD-ot tölti le, és ebből dönti el, hogy egyáltalán telepíthető-e a program:

MIDlet-Name: Progz Demo
MIDlet-Version: 1.0.0
MIDlet-Vendor: Progz
MIDlet-Jar-URL: progzdemo.jar
MIDlet-Jar-Size: 14823
MIDlet-1: Progz Demo, /icon.png, ProgzMidlet
MicroEdition-Profile: MIDP-2.0
MicroEdition-Configuration: CLDC-1.0

A MIDlet-Jar-Size legyen bájtra pontos. A Wireless Toolkit (most a 2.2-es verziót használom) a Project → Package paranccsal ezt automatikusan kitölti. Ha viszont utólag kézzel cserélsz valamit a JAR-ban, ne felejtsd el átírni ezt a számot is.

Mit érdemes tesztelni?

Az emulátorban minden szép, a valóságban nem. Három dolgot próbálok ki minden készüléken: hívás közben szünetel-e rendesen a program, mi történik, ha a játék közben becsukják a kagylót, és mekkora a legnagyobb JAR, amelyet a telefon elfogad. Ez utóbbi régebbi készülékeken akár 64 KB alatt is lehet.