4. Desarrollo de aplicaciones MIDP

Hasta ahora hemos visto la parte básica del lenguaje Java que podemos utilizar en los dispositivos móviles. Esta parte de la API está basada en la API básica de J2SE, reducida y optimizada para su utilización en dispositivos de baja capacidad. Esta es la base que necesitaremos para programar cualquier tipo de dispositivo, sin embargo con ella por si sola no podemos acceder a las características propias de los móviles, como su pantalla, su teclado, reproducir tonos, etc.

Vamos a ver ahora las APIs propias para el desarrollo de aplicaciones móviles. Estas APIs ya no están basadas en APIs existentes en J2SE, sino que se han desarrollado específicamente para la programación en estos dispositivos. Todas ellas pertenecen al paquete javax.microedition.

4.1. MIDlets

Los MIDlets son las aplicaciones para MIDs, realizadas con la API de MIDP. La clase principal de cualquier aplicación MIDP deberá ser un MIDlet. Ese MIDlet podrá utilizar cualquier otra clase Java y la API de MIDP para realizar sus funciones.

Para crear un MIDlet deberemos heredar de la clase MIDlet. Esta clase define una serie de métodos abstractos que deberemos definir en nuestros MIDlets, introduciendo en ellos el código propio de nuestra aplicación:

protected abstract void startApp();
protected abstract void pauseApp(); protected abstract void destroyApp(boolean incondicional);

A continuación veremos con más detalle qué deberemos introducir en cada uno de estos métodos.

4.1.1. Componentes y contenedores

Numerosas veces encontramos dentro de las tecnologías Java el concepto de componentes y contenedores. Los componentes son elementos que tienen una determinada interfaz, y los contenedores son la infraestructura que da soporte a estos componentes.

Por ejemplo, podemos ver los applets como un tipo de componente, que para poderse ejecutar necesita un navegador web que haga de contenedor y que lo soporte. De la misma forma, los servlets son componentes que encapsulan el mecanismo petición/respuesta de la web, y el servidor web tendrá un contenedor que de soporte a estos componentes, para ejecutarlos cuando se produzca una petición desde un cliente. De esta forma nosotros podemos deberemos definir sólo el componente, con su correspondiente interfaz, y será el contenedor quien se encargue de controlar su ciclo de vida (instanciarlo, ejecutarlo, destruirlo).

Cuando desarrollamos componentes, no deberemos crear el método main, ya que estos componentes no se ejecutan como una aplicación independiente (stand-alone), sino que son ejecutados dentro de una aplicación ya existente, que será el contenedor.

El contenedor que da soporte a los MIDlets recibe el nombre de Application Management Software (AMS). El AMS además de controlar el ciclo de vida de la ejecución MIDlets (inicio, pausa, destrucción), controlará el ciclo de vida de las aplicaciones que se instalen en el móvil (instalación, actualización, ejecución, desinstalación).

4.1.2. Ciclo de vida

Durante su ciclo de vida un MIDlet puede estar en los siguientes estados:

Será el AMS quién se encargue de controlar este ciclo de vida, es decir, quién realice las transiciones de un estado a otro. Nosotros podremos saber cuando hemos entrado en cada uno de estos estados porque el AMS invocará al método correspondiente dentro de la clase del MIDlet. Estos métodos son los que se muestran en el siguiente esqueleto de un MIDlet:

import javax.microedition.midlet.*;

public class MiMIDlet extends MIDlet {

protected void startApp()
throws MIDletStateChangeException { // Estado activo -> comenzar }

protected void pauseApp() { // Estado pausa -> detener hilos
}

protected void destroyApp(boolean incondicional)
throws MIDletStateChangeException { // Estado destruido -> liberar recursos
}
}

Deberemos definir los siguientes métodos para controlar el ciclo de vida del MIDlet:

Si ocurre un error que impida que el MIDlet empiece a ejecutarse deberemos notificarlo. Podemos distinguir entre errores pasajeros o errores permanentes. Los errores pasajeros impiden que el MIDlet se empiece a ejecutar ahora, pero podría hacerlo más tarde. Los permanentes se dan cuando el MIDlet no podrá ejecutarse nunca.

Pasajero: En el caso de que el error sea pasajero, lo notificaremos lanzando una excepción de tipo MIDletStateChangeException, de modo que el MIDlet pasará a estado pausado, y se volverá intentar activar más tarde.

Permanente: Si por el contrario el error es permanente, entonces deberemos destruir el MIDlet llamando a notifyDestroyed porque sabemos que nunca podrá ejecutarse correctamente. Si se lanza una excepción de tipo RuntimeException dentro del método startApp tendremos el mismo efecto, se destruirá el MIDlet.

Igual que en el caso anterior, si se produce una excepción de tipo RuntimeException durante la ejecución de este método, el MIDlet se destruirá.

Figura 1. Ciclo de vida de un MIDlet

Hemos visto que el AMS es quien realiza las transiciones entre distintos estados. Sin embargo, nosotros podremos forzar a que se produzcan transiciones a los estados pausado o destruido:

NOTA: La llamada a este método notifica que el MIDlet ha sido destruido, pero no invoca el método destroyApp para liberar los recursos, por lo que tendremos que invocarlo nosotros manualmente antes de llamar a notifyDestroyed.

4.1.3. Cerrar la aplicación

La aplicación puede ser cerrada por el AMS, por ejemplo si desde el sistema operativo del móvil hemos forzado a que se cierre. En ese caso, el AMS invocará el método destroyApp que nosotros habremos definido para liberar los recursos, y pasará a estado destruido.

Si queremos hacer que la aplicación termine de ejecutarse desde dentro del código, nunca utilizaremos el método System.exit (o Runtime.exit), ya que estos métodos se utilizan para salir de la máquina virtual. En este caso, como se trata de un componente, si ejecutásemos este método cerraríamos toda la aplicación, es decir, el AMS. Por esta razón esto no se permite, si intentásemos hacerlo obtendríamos una excepción de seguridad.

La única forma de salir de una aplicación MIDP es haciendo pasar el componente a estado destruido, como hemos visto en el punto anterior, para que el contenedor pueda eliminarlo. Esto lo haremos invocando notifyDestroyed para cambiar el estado a destruido. Sin embargo, si hacemos esto no se invocará automáticamente el método destroyApp para liberar los recursos, por lo que deberemos ejecutarlo nosotros manualmente antes de marcar la aplicación como destruida:

public void salir() {

    destroyApp(true);

    notifyDestroyed();

}

Si queremos implementar una salida condicional, para que el método destroyApp pueda decidir si permitir que se cierre o no la aplicación, podemos hacerlo de la siguiente forma:

public void salir_cond() {
    try {

        destroyApp(false);

        notifyDestroyed();
    } catch(MIDletStateChangeException e) {
    }

}

4.1.4. Parametrización de los MIDlets

Podemos añadir una serie de propiedades en el fichero descriptor de la aplicación (JAD), que podrán ser leídas desde el MIDlet. De esta forma, podremos cambiar el valor de estas propiedades sin tener que rehacer el fichero JAR.

Cada propiedad consistirá en una clave (key) y en un valor. La clave será el nombre de la propiedad. De esta forma tendremos un conjunto de parámetros de configuración (claves) con un valor asignado a cada una. Podremos cambiar fácilmente estos valores editando el fichero JAD con cualquier editor de texto.

Para leer estas propiedades desde el MIDlet utilizaremos el método:

String valor = getAppProperty(String key)

Que nos devolverá el valor asignado a la clave con nombre key.