MIDP (Mobile Information Device Profile)
Versión: 1.0, Septiembre, 2005

Manuel Gómez Olmedo
Web: http://decsai.ugr.es/~mgomez, Mail: mgomez@decsai.ugr.es
CLDCInicioMIDP



1.- Introducción

Como ya se ha comentado, CLDC proporciona la base para ejecutar aplicaciones JAVA en dispositivos con insuficientes recursos para poder ejecutar una máquina virtual completa, como la ofrecida por J2SE. Sin embargo, la realización de una aplicación normal precisa más facilidades que las ofrecidas por CLDC, ya que no permite interaccionar con los usuarios, ni con dispositivos de almacenamiento ni la conexión a red. Esto se debe a que CLDC pretende ser la capa base sobre la que se asienten un conjunto de perfiles que suplirán dichas limitaciones.

MIDP está especialmente pensado básicamente para teléfonos móviles, aunque también pueden desarrollarse con este perfil aplicacione para PDAs. La posición lógica de MIDP respecto a una arquitectura software en que se usa sería la indicada por la figura siguiente.

Esquema general de MIDP

Sun ofrece Wireless Toolkit, que ofrece implementaciones de referencia de MIDP para Windows, Solaris y Linux, así como un producto adicional para PDAs. Normalmente, los fabricantes de dispositivos construyen sus propias implementaciones a partir de la ofrecida por SUN. Las implementaciones propias normalmente incluyen facilidades para instalación, borrado y gestión de MIDlets. Estas funciones no se pueden ofrecer en una implementación de referencia, ya que son altamente dependientes de los dispositivos.

La implementación de referencia de MIDP añade clases adicionales al conjunto de librerías básicas de CDLC. Por esta razón, precisa de, como mínimo, 128 KB de memoria RAM para su almacenamiento. Además, precisa de al menos 32 KB disponibles para el funcionamiento del montón (heap). En la práctica esta limitación es muy exigente, por lo que los desarrolladores han de tener cuidado al reservar espacio para objetos y en evitar que permanezcan activas referencias a objetos que ya no son necesarias, de forma que el colector de basura pueda actuar en cuanto sea posible. Del mismo modo, en el perfil MIDP se especifican 8 KB de memoria no volátil para usarse como almacenamiento persistente, de forma que los MIDlets puedan almacenar información que no se pierda cuando el dispositivo se apague.

Otra característica importante a tener en cuenta, por las limitaciones que supone, tiene que ver con el pequeño tamaño de las pantallas de los dispositivos. La especificación indica que la pantalla debe tener el menos 96 píxels de ancho por 54 de alto. La pantalla debe trabajar, como mínimo, con dos colores. Los PDAs suelen proporcionar pantallas cuadradas de 160 píxels, pudiendo manejar 65536 colores.

Respecto a los dispositivos de entrada también hay gran diferencia entre dispositivos. De un lado, los dispositivos más sofisticados, que disponen de un completo teclado, o las agendas con sistema operativo PALM, que disponen de reconocedor de caracteres escritos; por otro, los teléfonos en que sólo se dispone de un reducido teclado, pensados para situaciones en que no es necesaria la introducción de grandes cantidades de datos. La especificación mínima de MIDP supone la obligación de permitir la entrada de valores numéricos del 0 al 9, junto con teclas de flechas y botón de selección.

Los dispositivos móviles deben ofrecer la posibilidad de conexión a red. Sin embargo, MIDP no supone que los dispositivos vayan a estar conectados de forma directa, ni que la red de conexión sea compatible con TCP/IP. Si obliga a que los vendedores ofrezcan soporte a HTTP 1.1, bien directamente o bien mediante algún protocolo.

Hemos de tener en cuenta que la versión de referencia de SUN no es un producto comercial. Se espera que los fabricantes adopten esta implementación de referencia como base para la implementación sobre sus propios productos. Algunos de los requisitos de MIDP respecto al entorno software en que se han de ejecutar los MIDlets se indica a continuación:

2.- Plataforma MIDP

Esta plataforma, de la que sólo haremos aquí unos breves comentarios, consta del conjunto de clases ofrecido por CLDC, junto con una serie de clases específicas de MIDP, integradas en la jerarquía de paquetes javax.microedition. Sólo mencionar algunas de las características especiales:

Por tanto, estas son las propiedades que MIDP cambia sobre las establecidas mediante CLDC.

3.- MIDlets y MIDlet suites

Las aplicaciones JAVA que se ejecutan en dispositivos que implementan MIDP se denominan MIDlets. Un MIDlet consta de al menos una clase JAVA, que debe derivar de la clase base abstracta javax.microedition.midlet.MIDlet. El tiempo de ejecución de un MIDlet viene controlado por una serie de métodos definidos en dicha clase, que todas las clases derivadas deben implementar.

Un grupo de MIDlets relacionados pueden agruparse en un MIDlet suite. Todos los MIDlets de un suite se agrupan e instalan en un dispositivo como si fuesen un único elemento, de forma que sólo pueden desinstalarse y eliminarse en conjunto. Los MIDlets agrupados en un suite comparten tanto recursos estáticos como dinámicos:

Como ejemplo de la forma en que los MIDlets de un suite comparten recursos, supongamos un suite que contiene una clase denominada Contador, destinada a mantener la cuenta del número de instancias de MIDlets del suite que se ejecutan en cada momento.

public class Contador {

    // Contador del numero de instancias

    private static int instancias;

    public static synchronized void incrementar(){
       instancias++;
    }

    public static synchronized void decrementar(){
       instancias--;
    }

    public static int obtener(){
      return instancias;
    } 
}

Una única instancia de esta clase se cargará en la máquina virtual, sin importar cuántos MIDlets de los que integren el suite están en ejecución en la máquina virtual. Esto significa que el mismo dato miembro estático instancias será usado por todos los MIDlets, por lo que los métodos incrementar y decrementar afectarán al mismo contador. En este caso es necesaria la sincronización para asegurar que las operaciones de incremento y decremento sean realmente atómicas.

Es importante indicar que los MIDlets deben empaquetarse antes de poderse instalar en los dispositivos de destino. Todo lo necesario de un suite debe empaquetarse en un archivo JAR. La información del paquete debe incluirse en un archivo de manifiesto. Esta información también estará especificada en otro archivo denominado descriptor de aplicaciones JAVA (JAD: java application descriptor), que se mantiene separado del archivo JAR.

Los archivos de manifiesto y JAD son archivos de texto con la siguiente estructura:

  nombre_atributo: valor_atributo

El nombre del atributo y su valor están separados por dos puntos, siendo el espacio adicional opcional. Todos los atributos que son relevantes para la instalación de MIDlets comienzan por el prefijo "MIDlet-". Una lista completa de atributos, junto con una breve descripción de sus valores asociados, aparece en la tabla siguiente. Los valores en las columnas JAD y JAR indican que el atributo es obligatorio (M), opcional (O) o ignorado (I).

Nombre de atributo
JAR
JAD
Valor y significado
MIDlet-Name
M
M
El nombre del suite integrado en el archivo JAR. El usuario podrá ver este nombre
MIDlet-Version
M
M
El número de versión del suite empaquetado en el archivo JAR. Los número de versión tienen la forma a.b.c, donde valores mayores indican versiones más recientes, teniendo los números de la izquierda mayor precedencia
MIDlet-Vendor
M
M
El nombre del fabricante del suite. Se trata de texto libre.
MIDlet-n
M
I
Atributo que describe cada uno de los MIDlets que integran el suite. El valor numérico n comienza en 1 y sirve para identidicar cada uno de los MIDlets
MIDlet-Description
O
O
Una descripción del suite, para servir de información a los usuarios
MIDlet-Icon
O
O
Un icono que representa al suite durante la instalación o configuración. Se debe tratar de una imagen en formato png (Portable Network Graphics)
MIDlet-Info-URL
O
O
Dirección URL de un archivo que contiene información adicional sobre el suite. El contenido del archivo se mostrará al usuario, de forma que éste pueda decidir si desea o no instalar dicho suite.
MIDlet-Data-Size
O
O
Mínima cantidad de espacio de almacenamiento persistente para que el suite funcione de forma correcta. Se trata de espacio usado por el suite para almacenar datos de larga duración. Se espcifica en bytes. Si no se proporciona este atributo, se asume que el suite no precisa almacenamiento persistente
MIDlet-Jar-URL
I
M
La dirección URL del archivo JAR que contiene el suite
MIDlet-Jar-Size
I
M
El tamaño del JAR que contiene el suite, en bytes
MIDlet-Install-Notify
I
O
Una dirección URL usada para indicar condiciones de error o de éxito en la instalación del suite. Se trata de un atributo no incluido en la especificación MIDP, pero soportado por J2ME.
MIDlet-Delete-Confirm
I
O
Mensaje a mostrar en caso de que el suite vaya a ser borrado del dispositivo. Con este atributo ocurre lo mismo que se indicó respecto al anterior: se trata de un atributo no incluido en la especificación de MIDP
MIDlet-specific attributes
O
O
Los desarrollados de MIDlets pueden proporcionar atributos específicos
MIDlet-specific attributes
O
O
Los desarrollados de MIDlets pueden proporcionar atributos específicos
MicroEdition-Profile
M
I
La versión de especificación MIDP con la que el suite puede trabajar. Cuando aparecen varias versiones deben separarse mediante espacios. Las versiones especificadas en este atributo se comparan con el valor de la propiedad microedition.profiles para determinar la compatibilidad del suite con la versión de MIDP disponible
MicroEdition-Configuration
M
I
La configuración J2ME precisada por el suite. El valor de este atributo se compara con la propiedad microedition.configuration para determinar la compatibilidad

Se aprecia que alguna información aparece duplicada en el archivo de manifiesto del JAR y en el archivo JAD. Veremos por qué esto es necesario. La función del archivo de manifiesto es indicar al dispositivo el nombre y versión del suite almacenado en el JAR, así como indicar cuáles de los archivos empaquetados se corresponden con cada uno de los MIDlets. Para usar esta información el dispositivo debe descargar el JAR y extraer el archivo de manifiesto. Una vez hecho esto, se pueden mostrar los valores de los atributos MIDlet-Name, MIDlet-Version, MIDlet-Vendor. También de los atributos opcionales MIDlet-Description y MIDlet-Icon. Estos atributos permiten al usuario decidir qué MIDlets hay que instalar. No obstante, el archivo JAR podría ser muy grande, por lo que podría llevar mucho tiempo recuperarlo en situaciones de conexiones lentas, por ejemplo. Para evitar este problema, algunos de los atributos del manifiesto, con información extra, se duplica en el archivo JAD. Esto permite descargar inicialmente el archivo JAD en lugar del JAR. De esta forma, se puede mostrar al usuario la información del suite antes de haber descargado el archivo de sus clases. El archivo JAD contendrá información contenida en el manifiesto y alguna particular. Típicamente, los atributos que aparecen en él son:

    MIDlet-Name
    MIDlet-Vendor
    MIDlet-Version
    MIDlet-Description
    MIDlet-Icon
    MIDlet-Info-URL
    MIDlet-Data-Size
    MIDlet-Jar-Size
    MIDlet-Jar-URL

El objetivo de estos atributos, como se indicó con anterioridad, es ofrecer información sobre los MIDlets incluidos en el suite. Supongamos que desarrollamos un suite denominado Notas que permiten al usuario acceso directo a sus notas desde un dispositivo MIDP. El suite contiene dos MIDlets: uno para ver las notas y otro para enviar información sobre posibles errores en ellas. Para desarrollar la clase se han usado métodos de utilidad, pertenecientes a la clase Utilidades. En definitiva, las clases involucradas serían:

El manifiesto para este suite podría ser el siguiente:

    MIDlet-Name: Notas
    MIDlet-Vendor: Programas Pepe
    MIDlet-Version: 1.0.1
    MIDlet-Description: Conjunto de midlets para ver notas
    MIDlet-Icon: /desarrollo/moviles/iconos/notas.png
    MIDlet-Info-URL: http://www.programaspepe.com/notas/info.html
    MIDlet-Data-Size: 512
    MicroEdition-Profile: MIDP-1.0
    MicroEdition-Configuration: CLDC
    MIDlet-1: Visualizador,/desarrollo/moviles/iconos/ver.png,desarrollo.moviles.VerNotas
    MIDlet-2: Errores,/desarrollo/moviles/iconos/error.png,desarrollo.moviles.ErrorNotas

En el archivo JAR correspondiente, el archivo de manifiesto aparecería como META.INF/MANIFEST.mf. El archivo JAR también contendría los siguientes archivos:

Conviene hacer algunos comentarios en relación al valor de los atributos y al contenido del archivo JAR:

El archivo JAD asociado a este MIDlet contendrá la siguiente información:

    MIDlet-Name: Notas
    MIDlet-Vendor: Programas Pepe
    MIDlet-Version: 1.0.1
    MIDlet-Description: Conjunto de midlets para ver notas
    MIDlet-Info-URL: http://www.programaspepe.com/notas/info.html
    MIDlet-Data-Size: 512
    MicroEdition-Jar-Size: 10132 
    MIDlet-Jar-URL: http://www.programaspepe.com/notas/Notas.jar

Este archivo contiene la información que la pantalla del dispositivo mostrará al usuario, junto con la dirección URL del archivo JAR. En este caso, los atributos comunes tienen el mismo valor tanto en el archivo de manifiesto como en el archivo JAD. Para que el suite sea protable, es preciso que el archivo JAR esté codificado usando la codificación iso-8859-1, ya que se precisa que todas las implementaciones de MIDP soporten esta codificación.

En tiempo de ejecución los MIDlets pueden acceder a los archivos del JAR asociado al suite y obtener información sobre los valores de los atributos.

4.- Ejecución de MIDlets

Simplemente, como comentario inicial, aunque se comentará más tarde, los MIDlets deben derivar de la clase abstracta javax.microedition.midlet.MIDlet, que contiene métodos destinados a controlar el tiempo de ejecución de los MIDlets. Todos los MIDlets deben tener un constructor público predeterminado (es decir, que no requiera argumentos). La estructura básica de un MIDlet se muestra a continuación:

    public class EjemploMidlet extends MIDlet{
       // Constructor: opcional. Bastaría con el constructor por
       // defecto

       public EjemploMidlet(){
       }

       // Método que iniciará la ejecución del MIDlet

       public void startApp() throws MIDletStateChangeException{
       }

       // Método que interrumpe la ejecución del MIDlet

       public void pauseApp(){
       }

       // Método para finalización del MIDlet

       public void destroyApp(boolean unconditional) 
                throws MIDletStateChangeException{
       }
    }

En todo momento, los posibles estados en que podría estar un MIDlet son:

Inicialmente, cuando se produce la carga de un MIDlet, éste estará en estado pausa. Cuando se produce la llamada al método startApp se produce el paso al estado activo. En cualquier instante, la plataforma MIDP podría poner a un MIDlet en estado de pausa. Por ejemplo, en los teléfonos móviles esto ocurrirá cuando se detecta una llamada entrante. Esto se producirá mediante una llamada al estado pauseApp. La vuelta al estado activo precisa de una nueva llamada a startApp. Cuando se precisa la finalización de un MIDlet se usará una llamada a destroyApp. Al producirse esta llamada se liberan los recursos que el MIDlet pudiera estar usando. Cuando el argumento de este método es true es esto lo que ocurre. Pero pudiera haber algunas situaciones en que este tipo de finalización no es conveniente; por ejemplo, por haber datos que aún no se han almacenado. En este caso, la llamada mediante el argumento false, se produce el lanzamiento de la excepción MIDletStateChangeException.

Para ilustrar el ciclo de vida un MIDlet y la forma en que se controla, a la vez que mostrar el funcionamiento de las herramientas de generación de MIDlets, vamos a considerar un ejemplo sencillo de MIDlet, caracterizado por:

Como no se sabe aún lo suficiente como para construir interfaces de usuario (aunque pronto se sabrá), todos los mensajes se enviarán a la salida estándar. Se incluye a continuación el código correspondiente, y seguidamente se verá cómo generar el suite, tanto de forma manual como automática.


import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class EjemploMIDlet extends MIDlet{
	// Constructor por defecto de la clase
	public EjemploMIDlet(){
		String color=getAppProperty("color");
		System.out.println("Construido MIDlet con color: "+color);
	}

	// Metodo para iniciar el funcionamiento del MIDlet
	public void startApp() throws MIDletStateChangeException{
		System.out.println("Metodo startApp");
	}

	// Metodo para detener el funcionamiento del MIDlet
	public void pauseApp(){
		System.out.println("Metodo pauseApp");
	}

	// Metodo para destruir el MIDlet
	public void destroyApp(boolean condition){
		System.out.println("Metodo destroyApp. Condicion: "+condition);
	}
}

Se trata de un MIDlet muy sencillo, que permitirá practicar con los procedimientos de generación de MIDlets, así como los posibles estados en que puede encontrarse un MIDlet en ejecución: pausa, activo y destruido. El código puede descarge en el enlace

EjemploMIDlet.java

4.1- Generación manual

En primer lugar, hemos de crear dos directorios temporales, que llamaremos tmpclasses y classes. Usaremos un directorio especial denominado midlet dentro del directorio ejemplos ya usado con anterioridad. Dentro de él se crean los dos subdirectorios anteriormente indicados, junto con otro para el código fuente: src.

El proceso debe comenzar compilando los archivos de código fuente. Para ello se usará el comando (estando situados en el directorio midlet:

javac -bootclasspath $HOME/java/WTK2.2/lib/cldcapi11.jar:$HOME/java/WTK2.2/lib/midpapi20.zip -d ./tmpclasses ./src/*.java

Esto ha generado los archivos .class en el directorio tmpclasses. Ahora hay que verificar el código, para asegurarnos de su compatibilidad con el perfil CLDC. Para ello se ejecuta el comando:

$HOME/java/WTK2.2/bin/preverify -classpath $HOME/java/WTK2.2/lib/cldcapi11.jar:$HOME/java/WTK2.2/midpapi.zip -d ./classes ./tmpclasses 

Esto hace que la clase verificada se ubique en el directorio classes. Para que la demo muestre la forma de incluir iconos, hemos usado uno para este MIDlet. Estará situado en el directorio res/icons, dentro de ejemplos/midlet. El icono puede descargarse aqui: ejemplo.png. Una vez verificadas las clases, hemos de escribir un archivo de manifiesto para este MIDlet. La información que debe contener MANIFEST.MF es la siguiente:

MIDlet-1: ejemplo, icons/ejemplo.png, EjemploMIDlet
MIDlet-Name: ejemplo
MIDlet-Vendor: softwarePepe 
MIDlet-Version: 1.0
MicroEdition-Configuration: CLDC-1.1
MicroEdition-Profile: MIDP-2.0

La información de este archivo ha de formar parte del archivo jar que contiene todas las clases. Este es el siguiente paso: empaquetar las clases, junto con el manifiesto y el icono en un archivo jar. Para ello, estando situados en el directorio midlet haremos:

jar cmf MANIFEST.MF ejemplo.jar -C ./classes . -C ./res .

Para comprobar que todo ha funcionado de forma correcta, conviene examinar el contenido del archivo jar generado, ejecutando

jar tf ejemplo.jar

La salida mostrada debe contener:

META-INF/
META-INF/MANIFEST.MF
EjemploMIDlet.class
icons/
icons/ejemplo.png

Además, como vimos, también es necesario disponer de un archivo jad, que en nuestro caso sería tal y como se muestra a continuación:

MIDlet-1: ejemplo, /icons/ejemplo.png, EjemploMIDlet
MIDlet-Jar-Size: XXXX
MIDlet-Jar-URL: ejemplo.jar
MIDlet-Name: ejemplo
MIDlet-Vendor: softwarePepe
MIDlet-Version: 1.0
MicroEdition-Configuration: CLDC-1.1
MicroEdition-Profile: MIDP-2.0
color: rojo

Con respecto a este archivo, cabe destacar que se ha indicado XXX en la propiedad MIDlet-Jar-Size. Se ha hecho para recordar que una vez generado el archivo jar, hemos de observar su tamaño (haciendo ls -al y observando su tamaño en bytes). Ese valor será el que habrá que dar a este atributo. Si no hay coincidencia entre el valor especificado en el archivo jad y el tamaño del archivo jar, no se podrá ejecutar el MIDlet.

Una vez generada esta información, lo único que queda es lanzar el emulador. Recordad que seguimos posicionados en el directorio midlet. Suponiendo la ruta de instalación que hemos indicado anteriormente, el comando a ejecutar será:

$HOME/java/WTK2.2/bin/emulator -classpath ejemplo.jar -Xdescriptor:ejemplo.jad 

Este comando lanza la ejecución del emulador, y aparecerá preparado para la ejecución del MIDlet asociado al archivo jad especificado. Las opciones indicadas sirven para:

Existen otras opciones que también pueden usarse. A continuación se indican las más importantes:

Una vez que el MIDlet esté en ejecución conviene activar el evento Pause usando la opción MIDlet del emulador. Así podemos observar la forma en que cambia de estado. Se recomienda, como ejercicio, repetir completa la generación del MIDlet usando el ejemplo que aparece a continuación:


import java.util.Timer;
import java.util.TimerTask;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class OtroMIDlet extends MIDlet{
  // Marca para indicar la primera llamada a startApp

  private boolean comienzo=false;

  // Hebra de fondo

  private Thread intervaloTiempo;

  // Contador de tiempo

  private int intervalo;
  
  // Temporizador

  private Timer temporizador;

  // Para que el temporizador ejecute una tarea

  private TimerTask tarea;

  // Constructor por defecto. Puede omitirse si no se incluye ningún
  // otro constructor

  public OtroMIDlet (){
    // Mensaje indicando que se ha producido la llamada al constructor

    System.out.println("Llamada al constructor");

    // Se obtiene el valor del intervalo de tiempo. Se toma de las
    // propiedades del manifiesto o del archivo JAV

    String tiempo=getAppProperty("Intervalo-Tiempo");

    // Se pasa a valor entero
    
    intervalo=Integer.parseInt(tiempo);

    // Mensaje de salida indicando el intervalo de tiempo leido

    System.out.println("Intervalo: "+intervalo);
    }

    // Método startApp, que será llamado cuando el MIDlet comience su
    // ejecución

    public void startApp() throws MIDletStateChangeException{
      // Comprobamos si se produjo llamada previa a este metodo
      // o no

      if (comienzo == false){
        // Se trata de la primera llamada. Se cambia el valor
        // la variable comienzo y se indica por la salida
        // estándar

        comienzo=true;

        System.out.println("Llamada inicial a startApp");
        System.out.println("Se inicia el temporizador");

        comenzarTemporizador();
      }
      else{

        // Se muestra por pantalla la situacion

        System.out.println("Llamada a startApp estando midlet en pausa");
      }

      // En cualquier caso, se da comienza a la hebra secundaria
      // Esto se hace en un bloque sincronizado, para asegurar
      // que se ejecuta de forma completa. Recordar que el tiempo
      // de ejecución podría estar repartido entre diferentes
      // MIDlets del mismo suite
      
      synchronized(this){//1
        // Se crea la hebra si es necesario

        if (intervaloTiempo == null){//2
        
          // Creacion de la hebra

          intervaloTiempo=new Thread(){//3

            // Se define el metodo run, punto de entrada a la hebra

            public void run(){//4
               // Mensaje indicando que la hebra esta en marcha

               System.out.println("Hebra en marcha");

               // Nos aseguramos que se trata de la hebra que nos
               // interesa, almacenada en el dato miembro thread
               // El método de pausa pondrá a null el dato miembro
               // thread y se saldrá del método

               while(intervaloTiempo == this){//5
                 // Es la hebra que nos interesa. La hebra se ocupa
                 // de imprimir un mensaje cada segundo (1000 mili segundos)

                 try{//6
                   // Hacemos espera de 1 segundo

                   Thread.sleep(1000);

                   // Tras la espera, se indica la vuelta a la normalidad

                   System.out.println("Hebra aun activa");
                   
                 }catch(InterruptedException e){};
               }//f5

               // Se indica que finaliza la hebra

               System.out.println("Hebra finalizada.....");
            }//f4
          };//f3
        }//f2
     }//ff1  

     // Una vez asegurada la existencia de la hebra, que comience
     // a funcionar
     
     intervaloTiempo.start();
   }

   // Método para detener la ejecución de la hebra

   public void pauseApp(){
     // Cuando este método se invoque desde el temporizador, se hara
     // que la hebra secundaria deje de funcionar

     System.out.println("Llamada al método pauseApp");

     synchronized(this){
       if (intervaloTiempo != null){
         intervaloTiempo=null;
       }
     }
   }

       
   // Método para destruir el MIDlet

   public void destroyApp(boolean condicion) throws MIDletStateChangeException{
      // Este método será llamado cuando se vaya a destruir el MIDlet

      System.out.println("Llamada al metodo destroyApp....");
  
      if (intervaloTiempo != null){
        Thread secundaria=intervaloTiempo;

        // La siguiente sentencia hace que se salga del bucle en el
        // metodo startApp
        
        intervaloTiempo=null;

        // Espera hasta que la hebra finaliza

        try{
          secundaria.join();
        }catch(InterruptedException e){};
      }

      // Se detiene el temporizador

      detenerTemporizador();
   }

   // Método para iniciar un temporizador

   private void comenzarTemporizador(){
      // Crea una tarea relacionada con el temporizador

      tarea=new TimerTask(){
        
      // Variable booleana para comprobar si esta detenido

      private boolean estaEnPausa=false;
        
      // Variable contadora

      private int contador=0;

         // Método asociado a la ejecución. Se hara mientras
         // contador sea menor de 6

         public void run(){
           // Mensaje indicando la temporizacion de la tarea

           System.out.println("Tarea temporizada y en ejecucion.....");
           System.out.println("Valor de cuenta...."+contador);

           // Control de la finalizacion

           if (contador++ == 4){
             // Se termina el MIDlet

             try{
                 EjemploMIDlet.this.destroyApp(true);
             }catch(MIDletStateChangeException e){};

             // Notificacion

             EjemploMIDlet.this.notifyDestroyed();
             return;
           }

           // En caso de no haber llegado al contador, se cambia
           // el estado del MIDlet

           if (estaEnPausa == true){
             // Se conmuta el estado

             System.out.println("Se pasa a estado de ejecución");

             EjemploMIDlet.this.resumeRequest();

             estaEnPausa=false;
           }
           else{
              // Se pasara a pausa

              System.out.println("Pasando a estado de pausa");

              estaEnPausa=true;

              EjemploMIDlet.this.pauseApp();

              EjemploMIDlet.this.notifyPaused();
           }
         }
      };

      // Crea un timer y lo temporiza

      temporizador=new Timer();

      temporizador.schedule(tarea,intervalo,intervalo);

      // Mensaje indicando comienzo de temporización

      System.out.println("Comienzo de temporización");
   }

   // Método para detener un temporizador

   private void detenerTemporizador(){

     if (temporizador != null){
        System.out.println("Final del proceso de temporización");
        temporizador.cancel();
     }
   } 
}

4.2- Uso de KToolbar

Aunque se han comentado las herramientas disponibles para poder ejecutar MIDlets desde línea de comandos, el uso de KToolbar facilitica mucho todas las operaciones necesarias para conseguir compilar y ejecutar un MIDlet. Seguiremos el proceso completo de trabajo sobre el MIDlet incluido anteriormente.

El primer paso consistirá en ejecutar la herramienta KToolbar. Esta aplicación se encuentra en la instalación de Wireless Toolkit. Para ejecutarla haremos:

/$HOME/java/WTK2.2/bin/ktoolbar

La ventana principal de KToolbar tiene la siguiente aparencia:

Ventana principal de KToolbar

Crearemos un proyecto de suite, en el que incluiremos el MIDlet generado con anterioridad. Para ello se pulsa en el botón New Project. Al pulsar sobre él aparecerá la siguiente ventana:

Generación de nuevo proyecto

Como se ve, hemos rellenado la información de ambos campos de texto. Como nombre del proyecto podemos elegir cualquiera, sin necesidad de coincidir con el nombre de la clase principal del MIDlet. En el segundo campo de texto hemos de especificar el nombre de la clase principal del suite que queremos ejecutar en el emulador. En este caso es EjemploMIDlet. En cuanto se pulsa el botón Create Project aparecerá una nueva ventana donde aparecen informaciones relativas al MIDlet.

Ventana de propiedades

Al mismo tiempo, en la ventana principal de KToolbar han aparecido varios mensajes:

Ventana principal, mensajes de advertencia

Estos mensajes indican que se ha creado la estructura de directorios necesaria para el proyecto, y que las clases que queramos formen parte del MIDleet habrá que ubicarlas en el directorio src; recursos adicionales (como iconos) irán en el directorio res y las librerías (si las hubiera) en el directorio lib. Como se ve, el directorio del proyecto se crea en el directorio de instalación de la herramienta, bajo el subdirectorio apps. En nuestro caso, la ruta completa será $HOME/java/WTK2.2/apps/ejemplo.

Por tanto, lo primero que haremos será copiar el archivo con la clase asociada al MIDlet en el directorio $HOME/java/WTK2.2/apps/ejemplo/src/. De la misma forma, el directorio icons que contiene el icono se llevará a $HOME/java/WTK2.2/apps/ejemplo/res. Y esto es todo lo necesario para poder generar y ejecutar el MIDlet. En primer lugar, procedemos a generar las clases a partir del código fuente. Para ello se pulsa el botón Buid. Si todo ha ido bien, aparecerá un mensaje indicado que la generación se realizó de forma correcta. En caso de haber errores, se mostrarán en la ventana principal de KToolbar.

generación completa

En cuanto se ha generado el MIDlet, podemos ejecutar mediante el botón Run. Al pulsarlo, aparecerá el emulador con el MIDlet listo para ejecución.

MIDlet ejecutando

En nuestro caso, el MIDlet necesita un valor de atributo para poder ejecutar. Si se observa el código, se comprobará que el funcionamiento depende de la propiedad color. Si se intenta manejar esta propiedad, sin haberla especificado de forma explícita, puede ocurrir un error como el mostrado a continuación:

Error de ejecución

¿Pero cómo se define este atributo?. Para ello usaremos el botón Settings. Cuando aparezca seleccionaremos la opción User Defined, que permitirá definir atributos propios. Sobre esta ventana, pulsaremos el botón Add.

Agregar propiedad

Al pulsar sobre Add aparecerá la ventana que hemos de usar para definir el valor de la propiedad deseada:

Nueva propiedad

Al pulsar sobre aceptar, la ventana de propiedades queda de la siguiente forma:

Definir valor para nueva propiedad

Pulsamos OK y ya podemos volver a ejecutar el MIDlet. Como la entrada salida no se ha explicado, los mensajes generados por System.out.print aparecerán en la ventana principal de KToolbar.

Mensajes en ventana principal

La depuración se puede realizar de la misma forma que se indicó al hablar de CLDC. En este caso, la herramienta proporciona todo lo necesario para comunicarse con la máquina virtual, incluyendo el proxy de depuración. Por tanto, sólo será necesario especificar el puerto en que se conectará el depurador. Para fijar este parámetro se selecciona la opción Project y después Debug, y aparecerá ña siguiente ventana:

Depuración

Ejercicio: probad con los MIDlets de demo que vienen con la herramienta. Activad la monitorización, activando las preferencias (Edit + Preferences + Monitor) deseadas.

5.- Entrega e instalación de MIDlets

Veremos aquí cómo se hace la instalación de un MIDlet desde un ordenador, mediante una conexión dedicada. Este modo de operación está especialmente pensado para PDAs, que normalmente se suelen conectar, de forma regular, a un ordenador para sincronizar los datos.

También consideraremos la instalación a partir de una red a la que el dispositivo se conecta. Este es la forma más normal de instalación de MIDlets. Esta forma de instalación se denomina en línea o también OTA provisioning. Esto ya no es parte, en realidad, de la especificación de MIDP.

5.1.- OTA provisioning

Mediante este procedimiento, los proveedores de MIDlets instalan los suites en servidores web y proporcionan enlaces a ellos. Los usuarios activan los enlaces que dan acceso a ellos y los descargan vía WAP o mediante navegadores web de los dispositivos.

El proceso comienza cuando el ususrio se conecta a una página del fabricante del suite. La página incluirá un enlace a los suites, donde se puede informar de su contenido y solicitar su instalación. Si el usuario desea instalarlo, activará un enlace de la forma:

    Pulse aquí para instalar este suite
  

Como se ve, el enlace apunta al archivo JAD del suite. La petición de instalación del suite se envía desde el navegador del teléfono móvil, pero será gestionada por el software de gestión de aplicaciones del dispositivo. Para que los navegadores puedan identificar de forma sencilla los archivos de tipo JAD, el servidor web está configurado para asociar a ellos el siguiente tipo MIME:

    text/app-descriptor
  

Cuando el dispositivo recibe este tipo de contenido, el software de gesstión de aplicaciones del dispositivo móvil muestra el contenido del descriptor de la aplicación, de forma que el usuario pueda decidir finalmente si instalar o no el MIDlet. Hasta este momento el usuario sólo ha descargado el archivo JAD asociado al MIDlet, por lo que no ha sido necesario esperar mucho tiempo. Como este archivo contiene el tamaño del archivo JAR asociado, el usuario decidirá si instalarlo o no. Si el usuario decide la instalación, el software de gestión de aplicaciones del dispositivo mira la propiedad MIDlet-Jar-URL del archivo JAD y envía una petición a dicha dirección URL, que el servidor web devolverá con una extensión MIME de tipo application/java-archive.

En este momento, el suite está instalado en el dispositivo y el usuario puede seleccionar MIDlets contenidos en el suite y proceder a su ejecucuión. Una vez que se ha hecho la instalación, puede que el software de gestión de aplicaciones comunique al servidor web si la instalación finalizó correctamente, o la razón de que no sea así. Para esta última comunicación se utiliza la propiedad MIDlete-Install-Notify.