17. Conexiones de red

En J2SE tenemos una gran cantidad de clases en el paquete java.net para permitir establecer distintos tipos de conexiones en red. Sin embargo, el soportar esta gran API no es viable en la configuración CLDC dedicada a dispositivos muy limitados. Por lo tanto en CLDC se sustituye esta API por el marco de conexiones genéricas (GCF, Generic Connection Framework), con el que se pretenden cubrir todas las necesidades de conectividad de estos dispositivos a través de una API sencilla.

17.1. Marco de conexiones genéricas

Los distintos dispositivos móviles pueden utilizar distintos tipos de redes para conectarse. Algunos utilizan redes de conmutación de circuitos, orientadas a conexión, que necesitarán protocolos como TCP. Otros utilizan redes de transmisión de paquetes en las que no se establece una conexión permanente, y con las que deberemos trabajar con protocolos como por ejemplo UDP. Incluso otros dispositivos podrían utilizar otras redes distintas en las que debamos utilizar otro tipo de protocolos.

El marco de conexiones genéricas (GFC) hará que esta red móvil subyacente sea transparente para el usuario, proporcionando a éste protocolos estándar de comunicaciones. La API de GFC se encuentra en el paquete javax.microedition.io. Esta API utilizará un único método que nos servirá para establecer cualquier tipo de conexión que queramos, por esta razón recibe el nombre de marco de conexiones genéricas, lo cuál además lo hace extensible para incorporar nuevos tipos de conexiones. Para crear la conexión utilizaremos el siguiente método:

Connection con = Connector.open(url);

En el que deberemos especificar una URL como parámetro con el siguiente formato:

protocolo:direccion;parámetros

Cambiando el protocolo podremos especificar distintos tipos de conexiones. Por ejemplo, podríamos utilizar las siguientes URLs:

"http://jtech.ua.es/pdm" Abre una conexión HTTP.
"datagram://192.168.0.4:6666" Abre una conexión por datagramas.
"socket://192.168.0.4:4444" Abre una conexión por sockets.
"comm:0;baudrate=9600" Abre una conexión a través de un puerto de comunicaciones.
"file:/fichero.txt" Abre un fichero.

Cuando especifiquemos uno de estos protocolos, la clase Connector buscará en tiempo de ejecución la clase que implemente dicho tipo de conexión, y si la encuentra nos devolverá un objeto que implemente la interfaz Connection que nos permitirá comunicarnos a través de dicha conexión.

CLDC nos proporciona interfaces para cada tipo genérico de conexión, pero las implementaciones reales de los protocolos pertenecen a los perfiles.

Figura 1. Componentes de GCF

El único protocolo que la especificación de MIDP exige que se implemente es el protocolo HTTP. Este protocolo pertenece a MIDP, y no a CLDC como era el caso de las clases genéricas anteriores. Distintos modelos de dispositivos pueden soportar otro tipo de conexiones, pero si queremos hacer aplicaciones portables deberemos utilizar HTTP.

17.2. Conexión HTTP

La conexión mediante el protocolo HTTP es el único tipo de conexión que sabemos que va a estar soportado por todos los dispositivos MIDP. Este protocolo podrá ser implementado en cada modelo de móvil bien utilizando protocolos IP como TCP/IP o bien protocolos no IP como WAP o i-Mode.

Figura 2. Gateway para protocolos no IP

De esta forma nosotros podremos utilizar directamente HTTP de una forma estándar sin importarnos el tipo de red que el móvil tenga por debajo.

Cuando establezcamos una conexión mediante protocolo HTTP, podemos hacer una conversión cast del objeto Connection devuelto a un subtipo HttpConnection especializado en conexiones HTTP:

HttpConnection con = 
    (HttpConnection)Connector.open("http://jtech.ua.es/datos.txt");

Este objeto HttpConnection contiene gran cantidad de métodos dedicados a trabajar con el protocolo HTTP, lo cuál facilitará en gran medida el trabajo de los desarrolladores.

HTTP es un protocolo de petición/respuesta. El cliente crea un mensaje de petición y lo envía a una determinada URL. El servidor analizará esta petición y le devolverá una respuesta al cliente. Estos mensajes de petición y respuesta se compondrán de una serie de cabeceras y del bloque de contenido. Cada cabecera tendrá un nombre y un valor. El contenido podrá contener cualquier tipo de información (texto, HTML, imágenes, mensajes codificados en binario, etc). Tendremos una serie de cabeceras estándar con las que podremos intercambiar datos sobre el cliente o el servidor, o bien sobre la información que estamos transmitiendo. También podremos añadir nuestras propias cabeceras para intercambiar datos propios.

Una vez creada la conexión, ésta pasará por tres estados:

La conexión nada más crearse se encuentra en estado de configuración. Pasará automáticamente a estado conectada cuando solicitemos cualquier información sobre la respuesta.

17.2.1. Lectura de la respuesta

Vamos a comenzar viendo cómo leer el contenido de una URL. En este caso no vamos a añadir ninguna información al mensaje de petición, ya que no es necesario. Sólo queremos obtener el contenido del recurso solicitado en la URL.

Imaginemos que queremos leer el fichero en la URL http://jtech.ua.es/datos.txt. Como primer paso deberemos crear una conexión con dicha URL como hemos visto anteriormente. Una vez tengamos este objeto HttpConnection abriremos un flujo de entrada para leer su contenido de la siguiente forma:

InputStream in = con.openInputStream();

Una vez hecho esto, la conexión pasará a estado conectada, ya que estamos solicitando leer su contenido. Por lo tanto en este momento será cuando envíe el mensaje de petición al servidor, y se quede esperando a recibir la respuesta. Con el flujo de datos obtenido podremos leer el contenido de la misma, al igual que leemos cualquier otro flujo de datos en Java.

Dado que en este momento ya se ha enviado el mensaje de petición, ya no tendrá sentido realizar modificaciones en la petición. Es por esta razón por lo que la creación del mensaje de petición debe hacerse en el estado de configuración.

Una vez hayamos terminado de leer la respuesta, deberemos cerrar el flujo y la conexión:

in.close();
con.close();

Con esto la conexión pasará a estado cerrada, liberando todos los recursos.

17.2.2. Mensaje de petición

En muchos casos podemos necesitar enviar información al servidor, como por ejemplo el login y el password del usuario para autentificarse en la aplicación web. Esta información deberemos incluirla en el mensaje de petición. Existen distintas formas de enviar información en la petición.

Encontramos los diferentes tipos de mensajes de petición soportados por MIDP:

HttpConnection.GET

Los parámetros que se envían al servidor se incluyen en la misma URL. Por ejemplo, podemos mandar un parámetro login en la petición de la siguiente forma:
http://jtech.ua.es/pdm?login=miguel

HttpConnection.POST Los parámetros que se envían al servidor se incluyen como contenido del mensaje. Tiene la ventaja de que se puede enviar la cantidad de datos que queramos, a diferencia del método GET en el que esta cantidad puede estar limitada. Además los datos no serán visibles en la misma URL, ya que se incluyen como contenido del mensaje.
HttpConnection.HEAD No se solicita el contenido del recurso al servidor, sólo información sobre éste, es decir, las cabeceras HTTP.

Podemos establecer uno de estos tipos utilizando el método setRequestMethod, por ejemplo para utilizar una petición POST haremos lo siguiente:

con.setRequestMethod(HttpConnection.POST);

Además podremos añadir cabeceras a la petición con el siguiente método:

con.setRequestProperty(nombre, valor);

Por ejemplo, podemos mandar las siguiente cabeceras:

c.setRequestProperty("IF-Modified-Since", 
    "22 Sep 2002 08:00:00 GMT");
c.setRequestProperty("User-Agent",
    "Profile/MIDP-1.0 Configuration/CLDC-1.0");
c.setRequestProperty("Content-Language", "es-ES");

Con esto estaremos diciendo al servidor que queremos que nos devuelva una respuesta sólo si ha sido modificada desde la fecha indicada, y además le estamos comunicando datos sobre el cliente. Indicamos mediante estas cabeceras estándar que el cliente es una aplicación MIDP, y que el lenguaje es español de España.

17.2.3. Envío de datos en la petición

Cuando necesitemos enviar datos al servidor mediante HTTP mediante nuestra aplicación Java, podemos simular el envío de datos que realiza un formulario HTML. Podremos simular tanto el comportamiento de un formulario que utilice método GET como uno que utilice método POST.

En el caso del método GET, simplemente utilizaremos una petición de tipo HttpConnection.GET e incluiremos estos datos codificados en la URL. Por ejemplo, si estamos registrando los datos de un usuario (nombre, apellidos y edad) podemos incluir estos parámetros en la URL de la siguiente forma:

HttpConnection con = 
    (HttpConnection)Connector.open("http://www.jtech.ua.es/aplic" +
      "/registraUsuario?nombre=Pedro&apellidos=Lopez+Garcia&edad=25");

Cada parámetro tiene la forma nombre=valor, pudiendo incluir varios parámetros separados por el carácter '&'. Como en la URL no puede haber espacios, estos caracteres se sustituyen por el carácter '+' como podemos ver en el ejemplo.

En el caso de que queramos simular un formulario con método POST, utilizamos una petición de tipo HttpConnection.POST y deberemos incluir los parámetros que enviemos al servidor como contenido del mensaje. Para ello deberemos indicar que el tipo de contenido de la petición es application/x-www-form-urlencoded, y como contenido codificaremos los parámetros de la misma forma que se utiliza para codificarlos en la URL cuando se hace una petición GET:

nombre=Pedro&apellidos=Lopez+Garcia&edad=25

De esta forma podemos enviar al servidor datos en forma de una serie de parámetros que toman como valor cadenas de texto. Sin embargo, puede que necesitemos intercambiar datos más complejos con el servidor. Por ejemplo, podemos querer serializar objetos Java y enviarlos al servidor, o enviar documentos XML.

Para enviar estos tipos de información podemos utilizar también el bloque de contenido, debiendo especificar en cada caso el tipo MIME del contenido que vamos a añadir. Ejemplos de tipos MIME que podemos utilizar para el bloque de contenido son:

application/x-www-form-urlencoded Se envían los datos codificados de la misma forma en la que son codificados por un formulario HTML con método POST.
text/plain Se envía como contenido texto ASCII.
application/octet-stream Se envía como contenido datos binarios. Dentro de la secuencia de bytes podremos codificar la información como queramos. Por ejemplo, podemos codificar de forma binaria un objeto serializado, utilizando un DataOutputStream.

Para establecer el tipo de contenido la cabecera estándar de HTTP Content-Type. Por ejemplo, si añadimos texto ASCII, podemos establecer esta cabecera de la siguiente forma:

con.setRequestProperty("Content-Type", "text/plain");

Para escribir en el contenido del mensaje de petición deberemos abrir un flujo de salida como se muestra a continuación:

OutputStream out = con.openOutputStream();

Podremos escribir en este flujo de salida igual que lo hacemos en cualquier otro flujo de salida, con lo que de esta forma podremos escribir cualquier contenido en el mensaje de petición.

Al abrir el flujo para escribir en la petición provocaremos que se pase a estado conectado. Por lo tanto deberemos haber establecido el tipo de petición y todas las cabeceras previamente a la apertura de este flujo, cuando todavía estábamos en estado de configuración.

17.2.4. Tipo y cabeceras de la respuesta

En estado conectado, además del contenido del mensaje de la respuesta, podemos obtener el estado de la respuesta y sus cabeceras. Los estados de respuesta se componen de un código y un mensaje y nos permitirán saber si la petición ha podido atenderse correctamente o si por el contrario ha habido algún tipo de error. Por ejemplo, posibles estados son:

HttpConnection.HTTP_OK 200 OK
HttpConnection.HTTP_BAD_REQUEST 400 Bad Request
HttpConnection.HTTP_INTERNAL_ERROR 500 Internal Server Error

Este mensaje de estado encabeza el mensaje de respuesta. Si el servidor nos devuelve un mensaje con código 200 como el siguiente:

HTTP/1.1 200 OK

Es que se ha procesado correctamente la petición y nos devuelve su respuesta. Si ha ocurrido un error, nos mandará el código y mensaje de error correspondiente. Por ejemplo, el error 400 indica que el servidor no ha entendido la petición que hemos hecho, posiblemente porque la hemos escrito incorrectamente. El error 500 nos dice que se trata de un error interno del servidor, no de la petición realizada.

Podemos obtener tanto el código como el mensaje de estado con los siguientes métodos:

int cod = con.getResponseCode(); 
String msg = con.getResponseMessage();

Los códigos de estado podemos encontrarlos como constantes de la clase HttpConnection como hemos visto para los tres códigos anteriores.

También podemos utilizar este objeto para leer las cabeceras que nos ha devuelto la respuesta. Nos ofrece métodos para leer una serie de cabeceras estándar de HTTP como los siguientes:

getLength content-length Longitud del contenido, o -1 si la longitud es desconocida
getType content-type Tipo MIME del contenido devuelto
getEncoding content-encoding Codificación del contenido
getExpiration expires Fecha de expiración del recurso
getDate date Fecha de envío del recurso
getLastModified last-modified Fecha de última modificación del recurso

Puede ser que queramos obtener otras cabeceras, como por ejemplo cabeceras propias no estándar. Para ello tendremos una serie de métodos que obtendrán las cabeceras directamente por su nombre:

String valor = con.getHeaderField(nombre);
int valor = con.getHeaderFieldInt(nombre);
long valor = con.getHeaderFieldDate(nombre);

De esta forma podemos obtener el valor de la cabecera o bien como una cadena, o en los datos que sean de tipo fecha (valor long) o enteros también podremos obtener su valor directamente en estos tipos de datos.

Podremos acceder a las cabeceras también a partir de su índice:

String valor = con.getHeaderField(int indice);
String nombre = con.getHeaderFieldKey(int indice);

Podemos obtener de esta forma tanto el nombre como el valor de la cabecera que ocupa un determinado índice.

Esta respuesta HTTP, además de un estado y una serie de cabeceras, tendrá un bloque que contenido que podremos leer abriendo un flujo de entrada en la conexión como hemos visto anteriormente. Normalmente cuando hacemos una petición a una URL de una aplicación web nos devuelve como contenido un documento HTML. Sin embargo, en el caso de nuestra aplicación MIDP este tipo de contenido no es apropiado. En su lugar podremos utilizar como contenido de la respuesta cualquier otro tipo MIME, que vendrá indicado en la cabecera content-type de la respuesta. Por ejemplo, podremos devolver una respuesta codificada de forma binaria que sea leída y descodificada por nuestra aplicación MIDP.

Tanto los métodos que obtienen un flujo para leer o escribir en la conexión, como estos métodos que acabamos de ver para obtener información sobre la respuesta producirán una transición al estado conectado.

17.3. Acceso a la red a bajo nivel

Como hemos comentado, el único tipo de conexión especificada en MIDP 1.0 es HTTP, la cual es suficiente y adecuada para acceder a aplicaciones corporativas. Sin embargo, con las redes 2.5G y 3G tendremos una mayor capacidad en las conexiones, nos permitirán realizar cualquier tipo de conexión TCP y UDP (no sólo HTTP) y además la comunicación podrá ser más fluida.

Para poder acceder a estas mejoras desde nuestra aplicación Java, surge la necesidad de que en MIDP 2.0 se incorpore soporte para tipos de conexiones a bajo nivel: sockets (TCP) y datagramas (UDP). Estos tipos de conexiones de MIDP 2.0 son optativos, de forma que aunque se encuentran definidos en la especificación de MIDP 2.0, no se obliga a que los fabricantes ni los operadores de telefonía lo soporten. Es decir, que la posibilidad de utilizar estas conexiones dependerá de que la red de telefonía de nuestro operador y el modelo de nuestro móvil las soporte.

Si intentamos utilizar un tipo de conexión no soportada por nuestro sistema, se producirá una excepción de tipo ConnectionNotFoundException.

17.3.1. Sockets

Los sockets nos permiten crear conexiones TCP. En este tipo de conexiones se establece un circuito virtual de forma permanente entre los dispositivos que se comunican. Se nos asegura que los datos enviados han llegado al servidor y que han llegado en el mismo orden en el que los enviamos. El inconveniente que tienen es que el tener un canal de comunicación abierto permanentemente consume una mayor cantidad de recursos.

Para abrir una conexión mediante sockets utilizaremos una URL como la siguiente:

SocketConnection sc = 
    (SocketConnection) Connector.open("socket://host:puerto");

Una vez abierta la conexión, podremos abrir sus correspondientes flujos de entrada y salida para enviar y recibir datos a través de ella:

InputStream in = sc.openInputStream();
OutputStream out = sc.openOutputStream();

Es posible también hacer que nuestro dispositivos actúe como servidor. En este caso utilizaremos una URL como las siguientes para crear el socket servidor:

ServerSocketConnection ssc = 
  (ServerSocketConnection) Connector.open("socket://:puerto");
ServerSocketConnection ssc = 
  (ServerSocketConnection) Connector.open("socket://");

En el primer caso indicamos el puerto en el que queremos que escuche nuestro servidor. En el segundo caso este puerto será asignado automáticamente por el sistema. Para conocer la dirección y el puerto donde escucha nuestro servidor podremos utilizar los siguientes métodos:

int puerto = ssc.getLocalPort();
String host = ssc.getLocalAddress();

Para hacer que el servidor comience a escuchar y aceptar conexiones utilizaremos el siguiente método:

SocketConnection sc = (SocketConnection) ssc.acceptAndOpen();

Obtendremos un objeto SocketConnection con el que podremos comunicarnos con el cliente que acaba de conectarse a nuestro servidor.

Debemos tener en cuenta que normalmente los móviles realizan conexiones puntuales cuando necesitan acceder a la red, y cada vez que se conecta se le asigna una nueva IP de forma dinámica. Esto hace difícil que un móvil pueda comportarse como servidor, ya que no podremos conocer a priori la dirección en la que está atendiendo para poder conectarnos a ella desde un cliente.

17.3.2. Datagramas

Cuando trabajemos con datagramas estaremos utilizando una conexión UDP. En ella no se establece un circuito virtual permanente, sino que cada paquete (datagrama) es enrutado de forma independiente. Esto produce que los paquetes puedan perderse o llegar desordenados al destino. Cuando la pérdida de paquetes o su ordenación no sea críticos, convendrá utilizar este tipo de conexiones, ya que consume menos recursos que los circuitos virtuales.

Para trabajar con datagramas utilizaremos una URL como la siguiente:

DatagramConnection dc = 
   (DatagramConnection) Connector.open("datagram://host:puerto");

En este caso no hemos abierto una conexión, ya que sólo se establecerá una conexión cuando se envíe un datagrama, simplemente hemos creado el objeto que nos permitirá intercambiar estos paquetes. Podemos crear un datagrama que contenga datos codificados en binario de la siguiente forma:

byte[] datos = obtenerDatos();
Datagram dg = dc.newDatagram(datos, datos.length);

Una vez hemos creado el datagrama, podemos enviarlo al destinatario utilizando la conexión:

dc.send(dg);

En el caso del servidor, crearemos la conexión de datagramas de forma similar, pero sin especificar la dirección a la que conectar, ya que dependiendo del cliente deberemos enviar los datagramas a diferentes direcciones.

DatagramConnection dc = 
   (DatagramConnection) Connector.open("datagram://:puerto");

El servidor no conocerá las direcciones de sus clientes hasta que haya recibido algún datagrama de ellos. Para recibir un datagrama crearemos un datagrama vacío indicando su capacidad (en bytes) y lo utilizaremos para recibir en él la información que se nos envía desde el cliente de la siguiente forma:

Datagram dg = dc.newDatagram(longitud);
dc.receive(dg);

Una vez obtenido el datagrama, podremos obtener la dirección desde la cual se nos envía:

 String direccion = dg.getAddress();

Ahora podremos crear un nuevo datagrama con la respuesta indicando la dirección a la que vamos a enviarlo. En este caso en cada datagrama se deberá especificar la dirección a la que se envía:

Datagram dg = dc.newDatagram(datos, datos.length, direccion);

El datagrama será enviado de la misma forma en la que se hacía en el cliente. Posteriormente el cliente podrá recibir este datagrama de la misma forma en que hemos visto que el servidor recibía su primer datagrama. De esta forma podremos establecer una conversación entre cliente y servidor, intercambiando estos datagramas.

17.4. Envío y recepción de mensajes

Podemos utilizar la API adicional WMA para enviar o recibir mensajes cortos (SMS, Short Message Service) a través del teléfono móvil. Esta API extiende GFC, permitiendo establecer conexiones para recibir o enviar mensajes. Cuando queramos enviar mensajes nos comportaremos como clientes en la conexión, mientras que para recibirlos actuaremos como servidor. La URL para establecer una conexión con el sistema de mensajes para ser enviados o recibidos a través de una portadora SMS sobre GSM tendrá el siguiente formato:

sms://telefono:puerto

Las clases de esta API se encuentran en el paquete javax.wireless.messaging. Aquí se definen una serie de interfaces para trabajar con los mensajes y con la conexión.

17.4.1. Envio de mensajes

Si queremos enviar mensajes, deberemos crear una conexión cliente proporcionando en la URL el número del teléfono al que vamos a enviar el mensaje y el puerto al que lo enviaremos de forma opcional:

sms://+34555000000
sms://+34555000000:4444

Si no especificamos el puerto se utilizará el puerto que se use por defecto para los mensajes del usuario en el teléfono móvil. Deberemos abrir una conexión con una de estas URLs utilizando GFC, con lo que nos devolverá una conexión de tipo MessageConnection

MessageConnection mc = 
    (MessageConnection)Connector.open("sms://+34555000000");

Una vez creada la conexión podremos utilizarla para enviar mensajes cortos. Podremos mandar tanto mensajes de texto como binarios. Estos mensajes tienen un tamaño limitado a un máximo de 140 bytes. Si el mensaje es de texto el número de caracteres dependerá de la codificación de éstos. Por ejemplo si los codificamos con 7 bits tendremos una longitud de 160 caracteres, mientras que con una codificación de 8 bits tendremos un juego de caracteres más amplio pero los mensajes estarán limitados a 140 caracteres.

WMA permite encadenar mensajes, de forma que esta longitud podrá ser por lo menos 3 veces mayor. El encadenamiento consiste en que si el mensaje supera la longitud máxima de 140 bytes que puede transportar SMS, entonces se fracciona en varios fragmentos que serán enviados independientemente a través de SMS y serán unidos al llegar a su destino para formar el mensaje completo. Esto tiene el inconveniente de que realmente por la red están circulando varios mensajes, por lo que se nos cobrará por el número de fragmentos que haya enviado.

Podremos crear el mensaje a enviar a partir de la conexión. Los mensajes de texto los crearemos de la siguiente forma:

String texto = "Este es un mensaje corto de texto";
TextMessage msg = mc.newMessage(mc.TEXT_MESSAGE);
msg.setPayloadText(texto);

Para el caso de un mensaje binario, lo crearemos de la siguiente forma:

byte [] datos = codificarDatos();
BinaryMessage msg = mc.newMessage(mc.BINARY_MESSAGE);
msg.setPayloadData(datos);

Antes de enviar el mensaje, podemos ver en cuántos fragmentos deberá ser dividido para poder ser enviado utilizando la red subyacente con el siguiente método:

int num_segmentos = mc.numberOfSegments(msg);

Esto nos devolverá el número de segmentos en los que se fraccionará el mensaje, ó 0 si el mensaje no puede ser enviado utilizando la red subyacente.

Independientemente de si se trata de un mensaje de texto o de un mensaje binario, podremos enviarlo utilizando el siguiente método:

mc.send(msg);

17.4.2. Recepción de mensajes

Para recibir mensajes deberemos crear una conexión de tipo servidor. Para ello en la URL sólo especificaremos el puerto en el que queremos recibir los mensajes:

sms://:4444

Crearemos una conexión utilizando una URL como esta, en la que no se especifique el número de teléfono destino.

MessageConnection mc = 
    (MessageConnection)Connector.open("sms://:4444");

Para recibir un mensaje utilizaremos el método:

Message msg = mc.receive();

Si hemos recibido un mensaje que todavía no hay sido leido este método obtendrá dicho mensaje. Si todavía no se ha recibido ningún mensaje, este método se quedará bloqueado hasta que se reciba un mensaje, momento en el que lo leerá y nos lo devolverá.

Podemos determinar en tiempo de ejecución si se trata de un mensaje de texto o de un mensaje binario. Para ello deberemos comprobar de qué tipo es realmente el objeto devuelto, y según este tipo leer sus datos como texto o como array de bytes:

if(msg instanceof TextMessage) {
String texto = ((TextMessage)msg).getPayloadText();
// Procesar texto
} else if(msg instanceof TextMessage) {
byte [] datos = ((BinaryMessage)msg).getPayloadData();
// Procesar datos
}

Hemos visto que el método receive se queda bloqueado hasta que se reciba un mensaje. No debemos hacer que la aplicación se quede bloqueada esperando un mensaje, ya que éste puede tardar bastante, o incluso no llegar nunca. Podemos solucionar este problema realizando la lectura de los mensajes mediante un hilo en segundo plano. Otra solución es utilizar un listener.

17.4.3. Listener de mensajes

Estos listeners nos servirán para que se nos notifique el momento en el que se recibe un mensaje corto. De esta forma no tendremos que quedarnos bloqueados esperando recibir el mensaje, sino que podemos invocar receive directamente cuando sepamos que se ha recibido el mensaje.

Para crear un listener de este tipo deberemos crear una clase que implemente la interfaz MessageListener:

public MiListener implements MessageListener {
public void notifyIncomingMessage(MessageConnection mc) {
// Se ha recibido un mensaje a través de la conexion mc
}
}

Dentro del método notifyIncomingMessage deberemos introducir el código a ejecutar cuando se reciba un mensaje. No debemos ejecutar la operación receive directamente dentro de este método, ya que es una operación costosa que no debe ser ejecutada dentro de los callbacks que deben devolver el control lo antes posible para no entorpecer el procesamiento de eventos de la aplicación. Deberemos hacer que la recepción del mensaje la realice un hilo independiente.

Para que la recepción de mensajes le sea notificada a nuestro listener deberemos registrarlo como listener de la conexión con:

mc.setMessageListener(new MiListener());

En WTK 2.0 tenemos disponible una consola WMA con la que podremos simular el envío y la recepción de mensajes cortos que se intercambien entre los emuladores, de forma que podremos probar estas aplicaciones sin tener que enviar realmente los mensajes y pagar por ellos.