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
.
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.
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).
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:
startApp()
: Este método se invocará
cuando el MIDlet pase a estado activo. Es aquí donde insertaremos el
código correspondiente a la tarea que debe realizar dicho 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 tipoRuntimeException
dentro del métodostartApp
tendremos el mismo efecto, se destruirá el MIDlet.
pauseApp()
: Se invocará cuando se pause
el MIDlet. En él deberemos detener las actividades que esté
realizando nuestra aplicación.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á.
destroyApp(boolean incondicional)
: Se invocará
cuando se vaya a destruir la aplicación. En él deberemos incluir
el código para liberar todos los recursos que estuviese usando el MIDlet.
Con el flag que nos proporciona como parámetro indica si la
destrucción es incondicional o no. Es decir, si incondicional
es true
, entonces se destruirá siempre. En caso de que
sea false
, podemos hacer que no se destruya lanzando la excepción
MIDletStateChangeException
desde dentro de este método.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:
notifyDestroyed()
: Destruye el MIDlet. Utilizaremos
este método cuando queramos finalizar la aplicación. Por ejemplo,
podemos ejecutar este método como respuesta a la pulsación del
botón "Salir" por parte del usuario.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 anotifyDestroyed
.
notifyPause()
: Notifica al AMS de que el MIDlet
ha entrado en modo pausa. Después de esto, el AMS podrá realizar
una llamada a startApp
para volverlo a poner en estado activo.resumeRequest()
: Solicita al AMS que el MIDlet
vuelva a ponerse activo. De esta forma, si el AMS tiene varios MIDlets candidatos
para activar, elegirá alguno de aquellos que lo hayan solicitado. Este
método no fuerza a que se produzca la transición como en los
anteriores, simplemente lo solicita al AMS y será éste quién
decida.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) { } }
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.