Desarrollo de aplicaciones para dispositivos

Anuncio
mailxmail - Cursos para compartir lo que sabes
Desarrollo de aplicaciones para
dispositivos inalámbricos
(J2ME)
Autor: Gilberto Nájera
1
mailxmail - Cursos para compartir lo que sabes
Presentación del curso
Curso que abarca el nivel de principiante a intermedio de la programación
J2ME para móviles y PDAs, así como la elaboración de páginas web para los mismos.
Incluye la elaboración de una página y una aplicación de hoja de cálculo al final.
2
mailxmail - Cursos para compartir lo que sabes
1. Introducción
1. INTRODUCCIÓN
Durante los últimos años hemos experimentado, más que en ninguna otra
época de la humanidad, un sorprendente avance en la tecnología; tanto en la de las
grandes organizaciones: comerciales, militares y educativas, como en la que usamos
diariamente.
Indudablemente algunos de los más grandes avances se han dado en el área
de las comunicaciones: televisión, radio, satélites, teléfonos celulares; y en la
electrónica y computación: computadoras con capacidades que antes eran
inimaginadas, dispositivos de tamaños minimizados sorprendentemente, sin
sacrificar por esto gran poder de procesamiento.
Hasta hace pocos años estas dos ramas de la tecnología se desarrollaban
independiente una de la otra, pero con el surgimiento de Internet, revolucionando la
forma de comunicarse, los teléfonos celulares o móviles fueron creciendo en cuanto
a capacidad, de forma que han llegado a convertirse -algunos modelos- en
computadoras de bolsillo, con las limitantes de poder que sus características
suponen, obviamente.
Quién habría de imaginarse hace treinta años que un teléfono podría
trasladarse de un lugar a otro, no requerir de cable y tener mayor funcionalidad que
las supercomputadoras de aquella época o que no sería necesario sino un bolsillo de
8 x 13 cm para cargar una computadora completa, seguramente nadie. Las nuevas
tecnologías utilizadas por los fabricantes de estos aparatos tienden a reducir el
tamaño sin sacrificar funcionalidad e incluso aumentarla.
Dentro de las nuevas tecnologías que incorporan los pequeños dispositivos
portátiles -PDA (Portable Digital Assistant), teléfonos celulares, etc.- se encuentran
primordialmente el acceso a Internet, con todas las ventajas de movilidad e
información que proporcionan ambas tecnologías (telefonía móvil e Internet), por un
lado y la capacidad para el usuario de crear sus propias aplicaciones o descargarlas
de Internet e instalarlas en su equipo.
En cuanto al acceso y navegación en Internet, se utiliza un protocolo llamado
WAP (Wireless Application Protocol) que es el encargado de la comunicación y la
interpretación de los contenidos; también se utiliza, para la creación de las páginas
3
mailxmail - Cursos para compartir lo que sabes
interpretación de los contenidos; también se utiliza, para la creación de las páginas
que se verán en estos dispositivos, el lenguaje WML (Wireless Markup Language), el
cual es similar al HTML de las computadoras de escritorio, aunque diseñado
especialmente para las características de los dispositivos portátiles. En este
documento se tratarán brevemente los fundamentos del diseño de páginas WML.
Por el lado de desarrollo de aplicaciones se tienen lenguajes como C++ y Java
en versiones especiales para los equipos en cuestión, el presente reporte de
investigación se centrará en el lenguaje Java, ya que ocupa una abrumadora mayoría
en el mercado y está demostrando ser la mejor opción disponible para todos los
programadores interesados en el tema, ademas de las características de seguridad y
estabilidad inherentes a cualquier producto Java.
1.1.Un vistazo a las comunicaciones inalámbricas
Este amplísimo campo abarca desde las señales de radio y televisión hasta
teléfonos celulares y comunicación por satélite. Conceptualmente, puede ser
dividido en dos tipos:
-LAN (Local Area Network): Operan regularmente en distancias cortas -unos
cuantos metros. Pueden ser desde el control de la alarma de un auto, un juguete de
radio control o una red BlueTooth.
-WAN (Wide Area Network): Sobrepasan por mucho las distancias de los
dispositivos LAN, aunque basan su funcionamiento en una red terrestre
excelentemente elaborada: Una red de antenas de radio cuidadosamente colocadas,
los aparatos pueden funcionar mientras se encuentren en el rango de al menos una
de las antenas. Como los teléfonos celulares y los receptores de radio.
1.2.Introducción a la plataforma Java
Java fue creado por Sun Microsystems en un intento por desarrollar un
lenguaje que facilitara la programación de aparatos electrodomésticos conectados a
la red de una casa inteligente. Después de un tiempo de desarrollo, la directiva del
proyecto vio que las capacidades del lenguaje iban mucho mas allá de una red
casera y decidieron enfocarlo hacia las aplicaciones basadas en Internet o con cierto
nivel de desempeño requerido en el trabajo de red, además de satisfacer a las
empresas que demandaban un lenguaje que se pudiese implementar en todas sus
máquinas, sin importar el sistema operativo sobre el que trabajaran.
4
mailxmail - Cursos para compartir lo que sabes
Aunque el lenguaje Java es sintácticamente similar a C++, difiere
enormemente en su fundamento:
-Java es un lenguaje puramente orientado a objetos, interpretado, esto
significa que los programas realizados con Java no son ejecutables por si solos,
existe otro programa que los interpreta cada vez que son requeridos. Esto puede
causar disminución en la velocidad de ejecución de las aplicaciones, pero, aunque
aun no se alcanza la rapidez de ejecución de los programas hechos en C++ o Visual
BASIC, se ha mejorado enormemente con respecto a las primeras versiones del
lenguaje, lo que significa que tal vez en un futuro cercano la rapidez deje de ser un
impedimento para usar Java.
-C++ utiliza, como característica principal, apuntadores a memoria y delega a
los programadores, simples seres humanos, la tarea de localizarla, controlarla y
liberarla. Mientras tanto, Java utiliza objetos de tipo seguro, no permite la
asignación de memoria dinámica y gracias a su recolector de basura (garbage
collector) la memoria sin usar se recicla automáticamente.
La base de la plataforma Java es una máquina virtual, la cual puede ser
implementada en los más populares sistemas operativos y en gran variedad de
hardware. Por lo que se puede tener aplicaciones binarias Java operando
consistentemente a través de diferentes implementaciones.
Las APIs (Application Programming Interface) Java son el conjunto de clases y
objetos que permiten, mediante la utilización del lenguaje Java, la interacción entre
el programador y la computadora y entre esta y el usuario final.
Juntos, el lenguaje de programación Java, la máquina virtual y las apis, forman
la plataforma Java (Fig. 1.1) , la cual, en su versión 2, se puede encontrar en tres
ediciones:
-Java 2 Standard Edition (J2SE). Diseñada para computadoras de escritorio,
puede trabajar en sistemas operativos como: Windows, Linux, MacOS, Solaris, OS x.
-Java 2 Enterprise Edition (J2EE). Plataforma para aplicaciones multiusuario o
empresariales. Se basa en J2SE y agrega apis para trabajo en el servidor.
-Java 2 Micro Edition (J2ME). Conjunto de tecnologías y especificaciones
desarrolladas para dispositivos pequeños como los teléfonos celulares y PDA (palm,
agendas electrónicas). Utiliza derivados de componentes J2SE, como son una
5
mailxmail - Cursos para compartir lo que sabes
máquina virtual más pequeña y un conjunto de apis menos potentes.
Figura 1.1. La plataforma Java
1.2.1.Introducción a J2ME
A diferencia de J2SE, la micro edición de Java no es sólo una pieza de software
ni una simple especificación, J2ME es una plataforma, una colección de tecnologías y
especificaciones diseñadas para diferentes partes del mercado de los dispositivos
pequeños.
J2ME se divide en configuraciones, perfiles y paquetes opcionales. Las
configuraciones son especificaciones que detallan una máquina virtual y un conjunto
base de apis que pueden ser usadas en cierta clase de dispositivos. La maquina
virtual puede ser completa, como la describe la especificación o algún derivado de
ella.
Un perfil trabaja sobre una configuración pero agrega apis específicas para
hacer un entorno completo de construcción de aplicaciones. Usualmente incluyen
apis para el ciclo de vida de las aplicaciones, interfaz de usuario y almacenamiento
persistente.
Un paquete opcional provee una funcionalidad que puede no estar
relacionada con alguna configuración o perfil, por ejemplo la API Bluetooh (JSR182).
Actualmente J2ME tiene disponibles dos configuraciones:
6
mailxmail - Cursos para compartir lo que sabes
Actualmente J2ME tiene disponibles dos configuraciones:
-Connected Limited Device Configuration (CLDC). Para dispositivos pequeños
con conexiones de red intermitentes, como teléfonos celulares y PDA.
-Connected Device Configuration (CDC). Diseñada para aparatos más grandes
(en términos de memoria y poder de procesamiento) con conexiones de red
robustas.
MIDP (Mobile Information Device Profile) es el perfil con mayor desarrollo en
la plataforma Java, aunque se está investigando el PDA Profile. MIDP incluye apis de
interfaz de usuario, de ciclo de vida de aplicaciones y algunas de almacenamiento
persistente.
Los dispositivos implementan una "pila" de software completa, la cual
consiste usualmente en una configuración, un perfil y apis opcionales.
Figura 1.2. Pila de software para JSR185
JSR185, Java Technnology for Wireless Industyry (JWTI), es la especificación
vigente para el desarrollo de aplicaciones inalámbricas en Java, con esta
especificación se intenta dejar de lado algunos problemas de compatibilidad entre
dispositivos que las anteriores especificaciones dejaron pendientes, así que JSR185
exige que todos los fabricantes que deseen adoptar esta especificación deben incluir
en sus dispositivos CLDC 1.0 o 1.1, MIDP 2.0 y WMA (Wireless Messaging API),
dejando opcional MMAPI (MultiMedia API), de esta forma el desarrollador puede
saber que esperar de una clase de dispositivos y con esto crear aplicaciones
altamente portables. En la figura 1.2 se presenta gráficamente esta pila de software.
1.3.Tecnología WAP y WML
1.3.1.WAP (Wireless Application Protocol)
Este protocolo pretende ofrecer a los usuarios de equipos con recursos de
7
mailxmail - Cursos para compartir lo que sabes
memoria, resolución de pantalla y capacidad de procesamiento limitados las mismas
funcionalidades que se ofrecen de Internet a través de computadoras personales,
pero con las ventajas y limitantes propias de estos dispositivos.
1.3.2.WML (Wireless Markup Language)
WML es un lenguaje de aumento o ganancia (markup) de texto basado en el
lenguaje de aumento extensible (XML, Extensible Markup Language) y fue
desarrollado para especificar contenidos e interfaces de usuario para terminales de
banda estrecha tales como teléfonos móviles e intérpretes de páginas. WML est`
diseñado para trabajar con dispositivos inalámbricos pequeños que poseen cuatro
características:
-Pantalla pequeña de baja resolución. Al nacimiento de este lenguaje, la
resolución de los teléfonos celulares se limitaba a unas cuantas lineas de texto y de
8 a 12 columnas de caracteres, debido al rápido aumento en ese ramo, las nuevas
versiones del lenguaje se pueden implementar en equipos con resoluciones de hasta
256 x 256 pixeles..
-Los aparatos de entrada tienen una capacidad limitada o están diseñados
para un propósito determinado. Un teléfono móvil tiene comúnmente teclas
numéricas y un reducido número de teclas adicionales con funciones especificas.
Algunos dispositivos más sofisticados pueden poseer teclas programables de
software, pero no un ratón ni otros dispositivos de selección.
-Los recursos computacionales están limitados por una CPU de baja potencia,
una memoria reducida y una potencia restringida.
-La red ofrece un reducido ancho de banda y una alta latencia. No son
infrecuentes los aparatos con conexiones de red de 300 bit/s a 10 Kb/s y latencia
"r ound-trip" de entre 5 y 10 segundos.
Las características del lenguaje WML pueden agruparse en cuatro áreas
principales:
-Ofrece un soporte de texto e imagen y tiene una amplia variedad de
formatos y comandos.
-Las cartas (fragmentos de página o pantallas) WML se agrupan en barajas.
Una baraja WML es similar a una página HTML identificada por una URL (Universal
Resource Location, localización universal de recursos) y es la unidad básica de
8
mailxmail - Cursos para compartir lo que sabes
transmisión de contenidos.
-Ofrece soporte para gestión de navegación entre cartas y barajas, e incluye
comandos para su manejo. Estos pueden usarse para navegar o ejecutar scripts,
WML también provee de conexiones de anclaje similares a las usadas en el HTML
versión 4.
-Se pueden establecer parámetros para todas las barajas de WML usando un
modelo establecido. Se pueden usar variables en lugar de cadenas y sustituirse en el
tiempo de ejecución. Esta forma de establecer parámetros permite que los recursos
de la red sean usados de forma eficiente.
Toda la información de WML se transmite en formato codificado por la red
inalámbrica.
9
mailxmail - Cursos para compartir lo que sabes
2. Diseño de páginas web para dispositivos
pequeños
2. DISEÑO DE PÁGINAS WEB PARA DISPOSITIVOS PEQUEÑOS
Como todos los interesados en el diseño y programación web saben, las
páginas web se hacen con los llamados lenguajes de aumento de texto,
dependiendo del tipo de página se utiliza un lenguaje determinado, aunque todos
tienen características similares.
2.1.Lenguajes de aumento o ganancia (markup) de texto
Los lenguajes de aumento de texto, como HTML (Hipertext Markup
Language), DHTML (Dynamic HTML), XML (eXtensible Markup Language) y WML
(Wireless Markup Language), son lenguajes que, basándose en la utilización de
etiquetas (tags), dan al texto simple características especiales, que pueden ir desde
mostrarlo con tamaño, alineación o tipo distinto de fuente hasta la creación de
enlaces a otros documentos o aplicaciones o bien la generación de formularios
gráficos para introducir datos a una base de datos, auxiliándose de otros lenguajes.
2.2.Lenguaje WML
Debido a que el tema que ocupa a este documento es la programación de
dispositivos inalámbricos, este capítulo se dedicará a la explicación del lenguaje de
diseño web dedicado a ellos, WML.
WML es un lenguaje basado en XML y creado especialmente para el diseño de
páginas web para equipos con capacidades de procesamiento, memoria e interfaz
limitadas, es decir, para las llamadas PDA, teléfonos celulares y paginadores (pagers).
2.2.1.Cartas y Barajas
Todo el código WML se organiza dentro de una colección de cartas y barajas.
Las cartas especifican una o más unidades de interacción con el usuario, por
ejemplo un menú de opciones, una pantalla de texto o un campo de entrada de
texto. Lógicamente, un usuario navega a través de una serie de cartas de WML,
revisando el contenido de cada una de ellas, introduciendo la información requerida,
realizando elecciones y moviéndose a otra carta. Las cartas están agrupadas dentro
de barajas. Una baraja es la unidad más pequeña WML que un servidor puede enviar
al terminal del usuario.
10
mailxmail - Cursos para compartir lo que sabes
al terminal del usuario.
2.2.2.Etiquetas
Al igual que HTML, WML basa su codificación en etiquetas, éstas definen el
formato y otras propiedades que tendrá el texto o los objetos que se encuentren
dentro de ellas.
Las etiquetas WML se señalan escribiéndose dentro de los signos <>, por
ejemplo <etiqueta>, aunque la mayoría de las etiquetas cuentan con apertura y
cierre, este último se hace agregando un "/" antes del nombre de la etiqueta y
después del signo "<", ejemplo: </cierre_de_etiqueta> .
Todo texto u objeto ubicado entre una apertura y un cierre de etiqueta se
verá afectado por ella, por ejemplo, <center>Texto</center>, hará que "Texto"
aparezca en el centro de la pantalla, así como cualquier imagen u objeto que se
encuentre dentro de los límites de la etiqueta <center></center>.
2.2.3.Etiquetas básicas
2.2.3.1.Obligatorias del documento
-<?xml version="1.0"?>. Con esta etiqueta se debe iniciar cualquier
documento WML, indica al navegador que se abrirá un archivo codificado en XML,
hay que recordar que es la base de WML.
-<!DOCTYPE WML PUBLIC"-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/WML_1.1.xml">. Se especifica el tipo de XML que
se abrirá, WML en este caso y además se indica al navegador la URL que seguirá
para la descodificación del lenguaje.
-<WML>... </WML>. Se escribe después de los dos anteriores, determina el
cuerpo principal del documento, cumple la misma función que la etiqueta <HTML>
en ese lenguaje.
-<card id="cartauno">...</card>. Recordemos que una página WML se divide
en cartas, esta etiqueta marca los límites de una carta llamada "cartauno". Una
página WML puede tener tantas cartas como sean necesarias.
2.2.3.2.De formato de texto
-<p>...</p>. Limita un párrafo, en WML todo texto debe estar incluido en un
párrafo.
11
mailxmail - Cursos para compartir lo que sabes
párrafo.
-<br/>. Salto de linea, como en HTML.
-<a href="#cartados">a la cartados</a>. Enlace de hipertexto a una carta
llamada cartados, en lugar de "#cartados" puede ir cualquier URL válida.
-<b> </b>. Pone el texto contenido en negritas.
-<i> </i>. Todo texto dentro de esta etiqueta se mostrará en itálicas.
-<em> </em>. Similar a la itálica.
-<strong> </strong>. Otra opción para letra negrita.
-<u> </u>. Texto subrayado.
-<big> </big>. Letra grande, se debe recordar que se está ante dispositivos
con limitantes gráficas, así que no se puede ser muy exigente con los tamaños de
letra.
-<small> </small>. Letra pequeña
2.2.3.3.Caracteres especiales
Al igual que en HTML, hay algunos caracteres utilizados por el lenguaje que
presentan ciertas dificultades a la hora de mostrarlos en pantalla (", $ #, etc.). En
caso de que sea necesario hacerlo se proporciona una lista, en la tabla 2.1, de
códigos por los que se deben sustituir los caracteres para que aparezcan sin ningún
problema.
Tabla 2.1. Códigos de caracteres especiales.
Signo Código
Signo
Código
<
< %3c
>
>%3e
'
'
"
"
&
&%26 $
$$%24
[
%5b
]
%5d
/
%2f
\
%55c
?
%3f
;
%3b
:
%3a
^
%5e
@
%40
|
%7c
12
mailxmail - Cursos para compartir lo que sabes
=
%3d
+
%2b
}
%7d
{
%7b
,
%2c
espacio %20
#
%23
`
%27
2.2.3.4.Tildes y eñes
En WML se deben sustituir las letras acentuadas y eñes por los siguientes
caracteres:
á
á
Á
`
é
É
É
í
i
ó
ó
Ó
Ó
ú
Ú
Ú
ñ
ñ
Ñ
é
Í
Í
ú
Ñ
2.2.4.Variables
Una de las grandes diferencias entre el WML y el HTML es que WML permite la
definición variables en las cartas, asignarles valores y presentarlos en la pantalla,
incluso utilizar las variables en expresiones. Con esto se puede conservar
información en el paso de una carta a otra y así dividir el contenido en varios pasos
(cosa bastante útil para pantallas tan pequeñas).
Las variables son cadenas de texto case-sensitive (distingue mayúsculas y
minúsculas) a la que se le asigna un valor (secuencia de caracteres) o ningún valor.
El nombre de la variable puede empezar por el guión bajo: "_" o una letra,
seguida de una o más letras, números o el guión bajo. Ejemplos de nombres de
variables válidos: NOMBRE_Usuario, _mivar1, X700II
2.2.4.1.Crear variables y asignarles valores
<setvar name="mivar1" value="Juan"/>. Juan es el valor de la variable mivar1.
Con setvar se crea la variable y a la vez se le asigna un valor.
Input. Con input se puede crear (declarar) la variable, asignarla un valor y
13
mailxmail - Cursos para compartir lo que sabes
Input. Con input se puede crear (declarar) la variable, asignarla un valor y
también devolverle el valor original. Input se utiliza para la entrada de datos y esos
datos (valores) se asignarán a una variable. Se presenta como un cuadro de texto.
Select. Con select se puede crear (declarar) la variable, asignarle un valor y
también devolverle el valor original. Select permite seleccionar al usuario uno o más
valores entre una lista de opciones que se asignarán a una variable.
Postfield. Con postfield se puede crear (declarar) la variable, asignarle un
valor y también devolverle el valor original.
2.2.4.2.Hacer referencia a variables
Es posible incluir el valor de una variable dentro de un documento WML, tanto
para que aparezca en la pantalla como para que se envíe a un programa. Hay tres
formas de hacer esto:
1 .$nombrevariable. Se utiliza cuando no hay ambigüedad con el nombre de la
variable dentro del contexto.
2 .$(nombrevariable). Cuando puede existir ambigüedad con el nombre de la
variable dentro del contexto.
3 .$(nombrevariable:conversión). Cuando se introduce el valor de una variable
dentro de una baraja, se puede definir el formato (escape, unescape o no escape) así:
1 .$(var:e), $(var:E), $(var:escape): cualquiera de los tres traduce al formato
escape[1].
2 .$(var:u), $(var:U), $(var:unesc): cualquiera de los tres traduce del formato
escape a texto US-ASCII.
3 .$(var:n), $(var:N), $(var:noesc): cualquiera de los tres hace que el valor de la
variable no se traduzca al formato escape.
WML siempre aplica el formato escape cuando se trata de atributos que
trabajan con URLs. Por eso muchas veces se puede asumir que el WML convertirá al
formato escape cuando lo tiene que hacer. De todas formas, conviene incluirlo si
tenemos mínimas dudas de lo que ocurrirá.
2.2.4.3.Las variables y las tareas <Noop>, <Prev>, <Refresh> y <Go>
- <noop/>. No hace nada. Se utiliza para desactivar eventos en el nivel de la
14
mailxmail - Cursos para compartir lo que sabes
baraja.
-<prev/>. Prev vuelve al anterior URL. Si dentro de prev hay un elemento
setvar, como en el ejemplo, se procesa antes de que se ejecute el prev.
<prev>
<setvar name="mivar" value="contenido"/>
</prev>
-<refresh/>. Refresh refresca los contenidos visibles del navegador. Si
refresh contiene un setvar, como en el ejemplo, se procesa el setvar y después se
procede al refresh.
<refresh>
<setvar name="mivar" value="contenido"/>
</refresh>
-<go>. Go lleva a otra URL u otra carta. Si dentro de Go hay un elemento
setvar, se procesa primero éste y después se ejecuta la tarea "Go".
<go> href= "http://www.WMLclub.com/cgi-bin/programa.pl
?x=$(mivar1)&y=$(mivar2)" method="post">
<setvar name="mivar1" value="50"/>
<setvar name="mivar2" value="80"/>
</go>
2.2.5.Creación de tablas
El manejo de tablas en WML es similar al de HTML, se define el elemento
<table> y se indican las columnas ("columns"), después se especifican, dentro de
cada columna, las filas y los datos que han de llevar.
Aunque el tag de <table> está recogido en las especificaciones del WML 1.1,
existen algunos móviles que no lo implementan, como es el caso del Nokia 7110, en
su lugar los datos son mostrados en una tabla de una columna. A continuación
vemos un ejemplo de código para creación de tablas:
<p><table columns="3">
15
mailxmail - Cursos para compartir lo que sabes
<tr>
<td><p>fila 1, columna 1</p></td>
<td><p>fila 1, columna 2</p></td>
<td><p>fila 1, columna 3</p></td>
</tr>
<tr>
<td><p>fila 2, columna 1</p></td>
<td><p>fila 2, columna 2</p></td>
<td><p>fila 2, columna 3</p></td>
</tr>
</table></p>
2.2.6.Manejo de imágenes
En los dispositivos móviles se permite la visualización de imágenes, pero
éstas deben estar en formato WBMP, en lugar de las utilizadas en HTML que pueden
ser gif, jpg o bmp; aunque algunos de los más modernos equipos ya cuentan con
soporte para jpg.
Una imagen WBMP puede ser creada por el diseñador en un programa
especial, o bien convertida de otro formato en el mismo programa. Uno de estos
programas puede ser Image Magic o bien el incluido en el kit de desarrollo WAP de
Nokia.
La forma de incluir una imagen en una página WML es la siguiente:
<p><img src="movil1.wbmp" alt="Movil1"/></p>
2.3.Scripts en WML (WMLS)
WMLS son las siglas de Wireless Markup Language Script y es un lenguaje de
programación que junto con el WML nos permite el diseño de las páginas que se
visualizan en los terminales móviles.
El navegador, además de poseer un intérprete para WML, también tiene uno
16
mailxmail - Cursos para compartir lo que sabes
para los scripts. Además del propio lenguaje en sí, este intérprete implementa un
conjunto de librerías que permiten a nuestros programas acceder a ciertos servicios
del terminal.
El código WMLS debe incluirse en un archivo distinto al WML asociado.
2.3.1.Utilización de WMLScript
Al utilizar WMLS se pretende cubrir los huecos de funcionalidad que deja
WML, es decir, se trata hacer con las páginas cosas que resultarían imposibles con el
simple WML. Entre las utilidades del WMLScript están las siguientes:
-Validación de los datos de entrada por parte del usuario.
-Manejar mediante código funciones propias del terminal, como realizar
llamadas desde el teléfono, enviar mensajes, agregar número de teléfono a la libreta
de direcciones y acceder a la tarjeta SIM.
-Realizar alertas, mensajes de error, confirmaciones, etc.
2.3.1.1.Identificadores
Se utilizan identificadores para nombrar las variables, las funciones y los
pragmas (información para la unidad de compilación). Estos identificadores tienen
las mismas reglas de definición que los nombres de variables en WML, además no
pueden ser iguales a una palabra reservada.
2.3.1.2.Comentarios
Los comentarios en el WMLS, son como en el C o en el C++ (una sola línea,
empieza por // y más de una línea empieza por /* y termina por */):
//comentario
/*Comentarios de más
de una línea
*/
2.3.1.3.Caracteres especiales
Al estar basado en WML, el manejo de caracteres especiales se realiza de la
misma forma.
NOTA: Los WMLscripts se pueden escribir en cualquier editor de texto normal
y corriente. No pueden ser ejecutados sin que sean llamados desde una carta WML.
17
mailxmail - Cursos para compartir lo que sabes
y corriente. No pueden ser ejecutados sin que sean llamados desde una carta WML.
2.3.2.Primeros pasos
A continuación se muestra un sencillo WMLS que convierte pesos a dólares
(suponiendo el tipo de cambio a 11.05 pesos por dólar).
Primero es necesario hacer el WML que recogerá la cifra que se desee pasar a
dólares.
<?xml version="1.0"?>
<!DOCTYPE WML PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/WML_1.1.xml">
<WML>
<card id="monedas" title="Cambio de divisas" newcontext="true">
<p>Conversor de pesos a dólares</p>
<p><input type="text" name="cantidad" title="Cantidad: "
format="*N"/></p>
<p><do type="accept">
<go href="peso-dolar.WMLs#calcular('$(cantidad)')"/>
</do>
</p>
<p>Resultado: $(resultado)</p>
</card>
</WML>
En esta carta se ha creado una variable que se llama cantidad y lleva la cifra a
convertir en dólares. Además se incluye una variable, que no tiene nada, llamada
resultado. En esta variable se almacenará el resultado en el WMLs siguiente:
extern function calcular(cantidad) {
var dolar = (cantidad/11.05);
18
mailxmail - Cursos para compartir lo que sabes
var dolarString = String.toString(dolar);
dolarString = String.format("%.2f", dolarString);
WMLBrowser.setVar("resultado", dolarString);
WMLBrowser.refresh();
}
Primero se declara la función, en este caso llamada calcular. La función es
extern, esto significa que puede ser llamada desde fuera del archivo WMLS. Esta
función recibirá como parámetro la variable cantidad. Al igual que C++ y Java, la
función comienza con { y termina con }.
Se declara una variable llamada dólar y se le asigna un valor, en este caso, lo
que haya introducido el usuario dividido entre 11.05 (el supuesto valor actual de la
moneda estadounidense).
Como el resultado puede tener una cantidad enorme de cifras decimales,
primero se convierte a una cadena de texto, con String.toString. El resultado irá
dentro de la variable dolarString, después con "%.2f" se indica que el formato será
un float (de punto flotante o decimal) con dos cifras después del punto.
Se crea una variable de nombre resultado y cuyo valor es el contenido de
dolarString y además se envía a la carta desde la cual fue llamada esta función.
Actualiza el navegador (browser) para que en la pantalla aparezcan los
cambios (ahora la variable "resultado" tiene un valor y tiene que aparecer en pantalla)
2.3.2.1.Variables en WMLScript
Las variables deben ser declaradas antes de poder utilizarlas en una
expresión. Esta es la forma de declarar una variable:
var NombreVariable;
El nombre de la variable puede llevar caracteres alfabéticos, números y el
guión bajo ("_"), pero nunca puede empezar con un número.
En la declaración de una variable puede asignársele un valor inicial. Hay dos
formas de hacerlo, con igual resultado:
var a; a = 1;
19
mailxmail - Cursos para compartir lo que sabes
O esta otra forma:
var a = 1;
También se pueden declarar varias variables a la vez, separadas por comas en
la misma instrucción var.
El valor de las variables en el WMLScript sólo duran lo que dura la función en
la que son declaradas. Por eso sólo se puede acceder a su valor dentro de la función
donde han sido declaradas.
Las variables pueden contener cualquier tipo de datos:
-Boolean (true o false).Ejemplo: var respuesta = true;
-Integer (número entero positivo o negativo, sólo desde -2147483648 hasta
el 2147483647). Ejemplo: var num = 7890;
-Float (número decimal, el mínimo es: +-1.17549435E-38 y el máximo es:
+-3.402823476E+38).Ejemplo: var num = 6.78;
-String (caracteres alfabéticos, se puede utilizar comillas dobles o
simples).Ejemplo: var nombre = "Juan";
-Invalid (para distinguirlo de los otros tipos de datos o para saber por
ejemplo cuándo el resultado de una operación se sale de los máximos o los
mínimos, un float menor a +-1.17549435E-38, por ejemplo).
Ejemplo: var ex = invalid;
Hay una forma de crear variables globales, esto es, variables que se
almacenan en el contexto del navegador y que pueden ser accesadas tanto por las
cartas WML, como por las funciones del WMLScript. Estas variables hay que crearlas
con la función setVar de la librería WMLBrowser, así:
WMLBrowser.setVar ("nombreVariable", valor);
La variable siempre tiene que tener un valor inicial:
WMLBrowser.setVar ("nombre", 0);
2.3.3.Librerías
WMLScript cuenta con seis librerías, éstas incluyen gran variedad de funciones
20
mailxmail - Cursos para compartir lo que sabes
con las que podemos trabajar, dichas librerías son: lang, float, string, URL, dialogue
y WMLbrowser.
2.3.3.1.Lang
Todas las funciones se utilizan como i se hiciera en un entorno orientado a
objetos: lang.abs(-9), por ejemplo. Todos los nombres de funciones comienzan con
minúscula.
1.Funciones aritméticas:
- abs (número). Calcula y devuelve el valor absoluto.
- m a x (número1, número2), min (número1, número2). Regresan el mayor y el
menor, respectivamente, entre dos números comparados.
2.Funciones de tipos de datos:
- isFloat (valor). Devuelve true si valor puede ser convertido en número con
decimales, false si no puede ser convertido e invalid si el sistema no soporta
decimales.
- isInt (valor). Es true si el valor puede ser convertido a entero. False si ni
puede ser. Invalid si excede los limites antes mencionados.
- parseFloat (cadena de texto). Devuelve un número con decimales, sacado de
la cadena de texto. Analiza la cadena de texto y termina en el primer carácter que
no sea parte de un número con decimales. Devuelve "invalid" cuando hay un error
en el análisis -no encuentra un número con decimales- o el dispositivo WAP no
acepta números con decimales.
- parseInt (cadena de texto). Devuelve el número entero, sacado de la cadena
de texto. El análisis de la cadena de texto se detiene en el primer carácter que no
sea parte de un número con decimales. Devuelve "invalid" cuando hay un error en el
análisis -no encuentra un número con decimales-.
3.Funciones de entorno.
Con estas funciones puedes conocer las capacidades del dispositivo WAP en
el que se ejecuta el WMLscript.
- CharacterSet(). Devuelve un número entero que es el valor asignado por el
IANA (Internet Assigned Numbers Authority) identificando el set de caracteres que
21
mailxmail - Cursos para compartir lo que sabes
soporta el intérprete del WMLScript.
- Float(). Devuelve true si el dispositivo soporta los números con decimales;
false, en caso contrario.
- MaxInt(). Devuelve el número entero máximo que acepta el dispositivo
(actualmente es 2.147.483.647).
- minInt(). Devuelve el número entero mínimo que acepta el dispositivo
(actualmente es -2.147.483.648)
4.Funciones de control de flujo.
Te permite terminar la ejecución del script y devolver el control al lugar desde
donde fue llamado (por ejemplo, otra función del mismo WMLscript).
- abort (cadena de texto). Termina "inesperadamente" la interpretación del
bytecode del WMLscript y devuelve una cadena de texto describiendo el error. A esta
cadena de texto no se puede acceder desde un documento WML.
- exit (valor). Termina "normalmente" la interpretación del bytecode del script
y devuelve un valor a la función desde la que fue llamado. A esta cadena de texto no
se puede acceder desde un documento WML.
5.Funciones de números aleatorios.
- random(número entero). Devuelve un entero positivo aleatorio entre 0 y el
número indicado.
- seed (número entero). Inicia en generador de números aleatorios en el
número indicado.
2.3.3.2.Float
1.Funciones de entorno:
- maxFloat(). Devuelve el número positivo decimal máximo. Actualmente es
3.40282347E+38, para todos los dispositivos que disponen de números decimales.
Si el dispositivo no los soporta, el resultado será "invalid".
- MinFloat(). Devuelve el número positivo decimal mínimo. Actualmente es
1.17549435E-38 para todos los dispositivos que disponen de números decimales.
Si el dispositivo no los soporta, devolverá "invalid".
22
mailxmail - Cursos para compartir lo que sabes
2.Funciones aritméticas.
- ceil(número). Devuelve el entero menor más próximo a número. Si el
número ya es entero, devuelve el mismo número dado.
- floor(número). Es lo contrario de ceil. Devuelve el entero más próximo a
número, pero que no sea mayor que él.
- i n t(número). De un número dado devuelve la parte entera.
- pow (número1, número2). Devuelve el número1 elevado a la potencia
número2. Si número1 es negativo, número2 debe ser un número entero. Si número1
es cero y número2 es menor que cero, pow devuelve "invalid".
- round(número). Devuelve el número entero más cercano al número dado. Si
dos enteros están igualmente cerca del número, round devuelve el mayor de ellos.
- sqrt(número). Devuelve la raíz cuadrada del número. Si el resultado es
menor que cero sqrt devuelve "invalid".
2.3.3.3.String
1.Funciones básicas.
- charAt (cadenadetexto, número). Devuelve el carácter en la posición número
de cadenadetexto.
- compare (cadenadetexto1, cadenadetexto2). Compara dos cadenas de texto.
- lenght (cadenadetexto). Regresa la cantidad de caracteres que tiene una
cadena.
- isEmpty (cadenadetexto). Devuelve true si la cadena está vacía, false si no lo
está.
2.Funciones de subcadena (Substring).
- subString(cadena,Inicio,Longitud). Extrae una subcadena de determinada
longitud de caracteres, comenzando en la posición Inicio.
- find (cadena, subcadena). Determina si una subcadena pertenece a una
cadena, devuelve True si existe.
- replace (cadena, cadena1, cadena2). Reemplaza en cadena todas las
apariciones de cadena1 por cadena2.
23
mailxmail - Cursos para compartir lo que sabes
apariciones de cadena1 por cadena2.
3.Funciones de elementos.
- elementAt (cadena, numElemento, separador).
- elements (cadena, separador).
- insertAt (cadena, cadena2, número, separador).
- removeAt (cadena, número, separador).
- replaceAt (cadena, elemento, número, separador).
4.Funciones de conversión:
- format (formatString, valor). Da un formato especifico a un determinado
valor.
- toString (valor). Devuelve el valor expresado como cadena de caracteres. Por
ejemplo:
a=String.toString(3.5); //a="3.5"
2.3.3.4.URL
La librería URL tiene funciones para manipular y verificar URLs relativos y
absolutos. La sintaxis de un URL (definido en detalle en [Request For Comments
(RFC) 2396]) es:
<scheme>://<host><:port>/<path>;<parameters>?<query>#<fragment>
Ejemplo:
http://www.WMLclub.com:80/docs/faqs/index.htm;3;2?id=juan#nombre
Para las funciones que devuelven una cadena que contiene un componente de
un URL, los delimitadores de adelante y de atrás no están incluidos en el resultado
devuelto. La única excepción es en la ruta (donde se incluyen las diagonales (/)).
Hay tres tipos de funciones de la librería URL:
1.Funciones para manejar URLs
Permiten validar, recuperar, crear y convertir a formato escape las URLs:
- escapeString(cadenadetexto).
24
mailxmail - Cursos para compartir lo que sabes
- getBase().
- GetReferer().
- isValid(url).
- resolve(baseString,incluidoString).
- unescapeString(cadenadetexto).
2.Funciones para extraer componentes.
Se utilizan para recuperar componentes individuales de URLs relativos y
absolutos. Estas funciones primero comprueban que el URL es válido, utilizando
isValid(), antes de extraer los componentes. Si isValid es "false", estas funciones
devolverán "invalid".
- getFragment(cadenadetexto).
- getHost(cadenadetexto).
- getPort(cadenadetexto).
- getParameters(cadenadetexto).
- getPath(cadenadetexto).
- getScheme(cadenadetexto).
- getQuery(cadenadetexto).
3.Funciones para extraer contenido.
Sólo hay una función para extraer contenido de un URL y es la que permite
asignar el contenido de un archivo de texto a una variable WMLScript.
loadString(URLString,contentTypeString)
2.3.3.5.Dialogue
Los diálogos son mensajes en pantalla que esperan una respuesta del usuario.
La forma en la que aparece en pantalla dependerá de cada micronavegador. Esta
librería sólo tiene tres funciones:
- prompt(cadena,cadenaPorDefecto). El texto que sale en pantalla es "cadena"
y espera una respuesta por parte del usuario (un input). El valor por defecto del
25
mailxmail - Cursos para compartir lo que sabes
y espera una respuesta por parte del usuario (un input). El valor por defecto del
input es cadenaPorDefecto. Devuelve la cadena que el usuario ha introducido.
- confirm(cadena,cadenaOK,cadenaCancelar). El texto que sale en pantalla es
"cadena" y despliega dos opciones a elegir ("cadenaOK" y "cadenaCancelar") y espera
una respuesta del usuario. La función confirm devuelve "true" si el usuario ha
seleccionado OK y "false" si ha seleccionado Cancel.
- alert(cadena). Presenta una cadena de texto y espera la confirmación del
usuario. Devuelve una cadena vacía.
2.3.3.6.WMLbrowser
Las funciones de esta librería permiten acceder a las variables de contexto del
micronavegador del dispositivo. También permiten ejecutar una tarea cuando el
intérprete WMLscript termina su ejecución.
Todas estas funciones devuelven invalid si el micronavegador no soporta WML
o si el intérprete de WMLscript no puede ser invocado desde el micronavegador.
1.Funciones de variables.
Permiten leer y escribir variables en el contexto del micronavegador.
- getVar(nombre). Devuelve la cadena que contiene la variable nombre,
devolverá una cadena vacía si la variable no existe, "invalid" si nombre no es un
nombre de variable apropiado. Ejemplo:
var a = "Hola";
var b = WMLBrowser.getVar(a);
// b = "Hola"
- setVar(nombre, valor). Inicia la variable nombre con el contenido de valor, si
la operación se realiza con éxito devuelve "True", de lo contrario "False" e "Invalid" si
nombre no es un nombre de variable válido. Por ejemplo:
var a = WMLBrowser.setVar(color,azul);
// a = true
2.Funciones de tareas.
- g o(cadenaURL). Carga el URL dado. Esto sucede cuando el script devuelve el
control al navegador. Ej.:
var carta="http://www.WMLclub.com/demos/a.WML#carta2";
26
mailxmail - Cursos para compartir lo que sabes
WMLBrowser.go(carta);
- prev(). Carga la carta vista anteriormente. Al igual que go(), su efecto se da
sólo cuando el WMLscript devuelve el control al micronavegador. Ej.:
WMLBrowser.prev();
- refresh(). Actualiza la carta visualizada. Ej.:
WMLBrowser.refresh();
- newContext(). Vacía el contexto del navegador y devuelve una cadena vacía.
Ej.:
WMLBrowser.newContext();
3.Funciones de consulta.
- GetCurrentCard(). Devuelve la carta actual (la que llamó al script) junto con la
ruta más corta con respecto a la ubicación del archivo .WMLs. Ej.:
Si www.server.com/a.WML llama a www.server.com/pro.WMLs:
var a = getCurrentCard();
// a = "a.WML"
O si www.serv0.com/a.WML llama a www.serv1.com/test/test.WMLs:
var a = getCurrentCard();
// a = "http://www.serv0.com/a.WML"
[1]Formato adoptado por Internet Society para identificar los caracteres
utilizados en las URL, donde se escribe el "%" y el número de carácter, %20 para el
espacio, por ejemplo.
27
mailxmail - Cursos para compartir lo que sabes
3. El lenguaje de programación Java
3. EL LENGUAJE DE PROGRAMACIÓN JAVA
Debido a que el presente documento centra su atención en la programación
de dispositivos móviles, solamente se dará un descripción general del
funcionamiento y características del lenguaje Java, ya que después se profundizará
en su variante J2ME.
Cuando se programa en Java, se coloca todo el código en métodos, de la
misma forma que se escriben funciones en lenguajes como C. En el presente
capítulo se describen algunas de las principales características del lenguaje de
programación Java.
3.1. Acerca de la máquina virtual
Como ya se ha mencionado, Java es un lenguaje interpretado, esto es que los
programas no se compilan a archivos ejecutables, sino a archivos que habrán de ser
interpretados por otros que si lo sean. Los archivos compilados de Java se llaman
archivos de códigos de bytes o bytecodes.
Cuando se hace un programa en Java, el o los archivos que contengan el
código fuente deben ser nombrados con extensión ".java" y al momento de ser
compilados se creará un archivo de bytecodes con extensión ".class" del mismo
nombre que el archivo fuente. Este archivo será la aplicación y, por lo tanto el que
se habrá de ejecutar.
El trabajo de la máquina virtual es interpretar estos códigos de bytes según el
sistema operativo en que se esté trabajando. He aquí el secreto de la portabilidad de
Java: existe una máquina virtual para cada sistema operativo y los bytecodes se
verifican cada vez que han de ser ejecutados, es decir, no corren directamente sobre
el sistema operativo, sino que la maquina virtual los ejecuta convirtiendo los
bytecodes a instrucciones propias de la plataforma anfitrión.
3.2.Características básicas del lenguaje
3.2.1. Comentarios
Los comentarios se introducen en los programas como ayuda para quienes
habrán de leerlos, ya sea los propios programadores o aquellos quienes les han de
28
mailxmail - Cursos para compartir lo que sabes
dar mantenimiento. Un comentario no se ejecuta ni compila junto con el resto del
programa, es únicamente para información. En Java hay tres tipos de comentarios:
A) // comentarios para una sola línea.
Todo el texto de una línea que se encuentre después de "//" será omitido por
el compilador.
B) /* comentarios de una o
más líneas*/
Se omitirá en la compilación y ejecución todo el texto contenido a partir de la
apertura del comentario "/*" y hasta su cierre "*/".
C) /** comentario de documentación,
de una o más líneas*/
Los comentarios de documentación, colocados inmediatamente antes de una
declaración (de variable o función), indican que ese comentario ha de ser colocado
en la documentación que se genera automáticamente cuando se utiliza la
herramienta javadoc. Dichos comentarios sirven como descripción del elemento
declarado permitiendo generar una documentación de nuestras clases escrita al
mismo tiempo que se genera el código.
En este tipo de comentario para documentación, se permite la introducción
de algunos tokens o palabras clave, que harán que la información que les sigue
aparezca de forma diferente al resto en la documentación.
3.2.2. Identificadores
Los identificadores se utilizan para nombrar variables, funciones, clases y
objetos; cualquier cosa que el programador necesite identificar o usar.
En Java, un identificador debe comenzar con una letra, un subrayado (_) o un
símbolo de dólar ($). Los siguientes caracteres pueden ser letras o dígitos. Se
distinguen las mayúsculas de las minúsculas (case sensitive) y no hay longitud
máxima en el identificador.
Serían identificadores válidos:
identificador
29
mailxmail - Cursos para compartir lo que sabes
nombre_usuario
Nombre_Usuario
_variable_del_sistema
$transaccion
Y su uso sería, por ejemplo:
int contador_principal;
char _lista_de_archivos;
float $cantidad_en_Ptas;
3.2.3. Palabras clave
Las palabras claves en un lenguaje de programación son aquellas que tienen
alguna función especifica para dicho lenguaje, por ejemplo una palabra que indique
la impresión de una cadena de texto en pantalla.
Las siguientes son las palabras clave que están definidas en Java y que no se
pueden utilizar como identificadores:
abstract
continue
for
new
boolean
default
goto
null
do
if
package
switch
synchronized
break
this
byte
double
implements
private
threadsafe
byvalue
else
import
protected
throw
case
extends
instanceof
public
return
transient
catch
false
int
char
final
interface
class
finally
long
static
void
const
float
native
super
while
short
true
try
Seguramente no serán estas las únicas palabras que no podrán ser utilizadas
30
mailxmail - Cursos para compartir lo que sabes
como identificadores, debido a que existen también las palabras utilizadas por las
clases que utilicemos (extendamos) en nuestros programas y las palabras reservadas
de Java.
3.2.4. Palabras Reservadas
Además de las anteriores, el lenguaje se reserva unas cuantas palabras más,
pero que hasta ahora no tienen un cometido específico. Esas palabras son:
cast
future
operator
generic
outer
rest
inner
var
3.2.5. Literales y tipos de datos
Java utiliza cinco tipos de datos básicos:
A) Enteros. Números sin punto decimal.
byte
8 bits
complemento a dos
short
16 bits
complemento a dos
int
32 bits
complemento a dos
long
64 bits
complemento a dos
Por ejemplo: 21, 077, 0xDC00
B) Reales en coma flotante. Números con punto decimal.
float
32 bits
IEEE 754
double
64 bits
IEEE 754
Por ejemplo: 3.14, 2e12, 3.1E12
C) Lógicos o booleanos. almacenan sólo valores de falso o verdadero.
true (verdadero).
false (falso).
D) Caracteres. Únicamente almacenan un carácter, una letra, símbolo o
número (no se toma en cuenta su valor matemático).
Por ejemplo:
"a", \t, \u????
[????] es un número unicode.
31
mailxmail - Cursos para compartir lo que sabes
E) Cadenas. Son una agrupación de caracteres.
Por ejemplo: "Esto es una cadena literal"
3.2.5.1.Arreglos
Son asociaciones de varios datos del mismo tipo, por ejemplo un arreglo de
cinco datos enteros o una lista de diez nombres, también son llamados arrays.
En Java se pueden declarar arreglos de cualquier tipo:
char s[];
int iArray[];
Incluso se pueden construir arreglos de arreglos:
int tabla[][] = new int[4][5];
Los límites de los arreglos se comprueban en tiempo de ejecución para evitar
desbordamientos y la corrupción de memoria, es decir, se verifica que no se
intenten ocupar más espacios de los especificados en la declaración del arreglo.
En Java un arreglo es realmente un objeto, porque tiene definido el operador
"[ ]". Tiene una función miembro: length. Se puede utilizar este método para
conocer su longitud.
int a[][] = new int[10][3];
a.length;
/* 10 */
a[0].length;
/* 3 */
Para crear un arreglo en Java hay dos métodos básicos. Crearlo vacío:
int lista[] = new int[50];
o se puede crear el arreglo con sus valores iniciales:
String nombres[] = {
"Juan","Pepe","Pedro","María"
};
Esto que es equivalente a:
32
mailxmail - Cursos para compartir lo que sabes
String nombres[];
nombres = new String[4];
nombres[0] = new String( "Juan" );
nombres[1] = new String( "Pepe" );
nombres[2] = new String( "Pedro" );
nombres[3] = new String( "María" );
No se pueden crear arreglos estáticos en tiempo de compilación:
int lista[50]; // generará un error en tiempo de compilación
Tampoco se puede llenar un array sin declarar el tamaño con el operador new:
int lista[];
for( int i=0; i < 9; i++ )
lista[i] = i;
Es decir, todos los arrays en Java son estáticos, esto mejora la seguridad en el
manejo de memoria. Para convertir un arreglo en el equivalente a los arreglos
dinámicos de C++, se usa la clase vector, que permite operaciones de inserción,
borrado, etc.
3.2.6. Operadores
Los operadores de Java son muy parecidos en estilo y funcionamiento a los de
C. En la siguiente tabla aparecen los operadores que se utilizan en Java, por orden
de precedencia:
.
[]
~
instanceof
-
()
*
++
/
<<
>>
-%
!
+
>>>
<
>
<=
>=
==
!=
&
^
|
&&
||
?:
=
op=
*=
/=
%=
+=
33
-=
mailxmail - Cursos para compartir lo que sabes
Los operadores numéricos se comportan como esperamos:
int + int = int
Los operadores relacionales devuelven un valor booleano.
Para las cadenas, se pueden utilizar los operadores relacionales para
comparaciones además de + y += para la concatenación:
String nombre = "nombre" + "Apellido";
El operador = siempre hace copias de objetos, marcando los antiguos para
borrarlos, y ya se encargará el garbage collector de devolver al sistema la memoria
ocupada por el objeto eliminado.
3.2.7. Separadores
Los separadores en Java definen la forma y función del código. Agrupan
bloques de código e identificadores, definen la prioridad en operaciones. Los
separadores admitidos en Java son los siguientes:
-() - paréntesis. Para contener listas de parámetros en la definición y llamada
a métodos. También se utiliza para definir precedencia en expresiones.
-{} - llaves. Para contener los valores de matrices iniciadas automáticamente.
También se utiliza para definir un bloque de código, para clases, métodos y
ámbitos locales.
-[] - corchetes. Para declarar tipos matriz. También se utiliza cuando se
referencian valores de matriz.
-; - punto y coma. Separa sentencias.
-, - coma. Separa identificadores consecutivos en una declaración de
variables. También se utiliza para encadenar sentencias dentro de una sentencia f o r.
-. - punto. Para separar nombres de paquete de subpaquetes y clases.
También se utiliza para separar una variable o método de una variable de referencia.
3.2.8. Control de flujo
Las instrucciones de control de flujo son aquellas que, basándose en criterios
dados por el programador, alteran el flujo natural (lineal) del programa.
34
mailxmail - Cursos para compartir lo que sabes
En Java tenemos casi las mismas instrucciones de control de flujo (selección,
decisión, salto y repetición) que en C.
3.2.8.1.Estructuras de decisión y selección
Son fragmentos de código que se ejecutan solamente si una condición
contenida en ellos se cumple, como se muestra en los siguientes ejemplos:
A) if/else:
if( condición_Boolean ) {
Instrucciones_si_se_cumple_la_condición_Boolean;
}
else {
Instrucciones_si:no_se_cumple;
}
B) switch:
switch( expr1 ) {
case expr2:
instrucciones_si_expr1_=_expr2;
break;
case expr3:
instrucciones_si_expr1_=_expr3;
break;
default:
instrucciones_si_expr1_<>_expr2_y_a_expr3;
break;
}
3.2.8.2.Ciclos de repetición
35
mailxmail - Cursos para compartir lo que sabes
Estas estructuras repiten su ejecución tantas veces como se haya indicado o
bien cuando se cumpla una determinada condición.
A) for
for( expr1 inicio; expr2 test; expr3 incremento ) {
sentencias;
}
También se soporta el operador coma (,) en los ciclos for
for( a=0,b=0; a < 7; a++,b+=2){ }
B) while
while( Boolean ) {
sentencias;
}
C) do/while
do {
sentencias;
}while( Boolean );
3.2.8.3.Excepciones
Algunas veces los programas producen algún error en tiempo de ejecución y
quisiéramos tener la capacidad de decirle qué hacer cuando esto suceda,
principalmente para evitar que el programa se colapse cada vez que el error se
presenta. para esto nos sirven las excepciones de Java.
A) try-catch-throw
try {
sentencias;
} catch( Exception ) {
sentencias;
36
mailxmail - Cursos para compartir lo que sabes
}
Java implementa excepciones para facilitar la construcción de código robusto.
Cuando ocurre un error en un programa, el código que encuentra el error lanza una
excepción, que se puede capturar y recuperarse de ella. Java proporciona muchas
excepciones predefinidas.
3.2.8.4.Control general del flujo
Rompen el flujo natural del programa incondicionalmente, ya sea para
terminar un módulo o todo el programa o simplemente para pasar a otro lugar del
código.
break [etiqueta]
continue [etiqueta]
return expr;
etiqueta: sentencia;
Por ejemplo, en caso de que nos encontremos con ciclos anidados, se permite
el uso de etiquetas para poder salirse de ellos, por ejemplo:
uno: for( ) {
dos: for( ) {
continue;
/* seguiría en el ciclo interno, pero omitiría las
instrucciones siguientes*/
continue uno;
break uno;
// seguiría en el ciclo uno principal
// se saldría del ciclo principal
}
}
En el código de una función siempre hay que ser consecuentes con la
declaración que se haya hecho de ella. Por ejemplo, si se declara una función para
que devuelva un entero, es imprescindible que se coloque un return final para salir
de esa función, independientemente de que haya otros en medio del código que
también provoquen la salida de la función. En caso de no hacerlo se generará un a
37
mailxmail - Cursos para compartir lo que sabes
advertencia (Warning), y el código Java no se puede compilar con "Warnings".
int func() {
if( a == 0 )
return 1;
return 0;
// es imprescindible porque se retorna un entero
}
3.2.9. Clases
Las clases son la parte fundamental de Java. Todo en Java forma parte de una
clase, es una clase o describe como funciona una clase. por lo tanto, el
conocimiento de las clases es fundamental para poder entender los programas Java.
Todas las acciones de los programas Java se colocan dentro del bloque de
una clase o un objeto, todos los métodos se definen dentro del bloque de la clase.
Java no soporta funciones o variables globales, todo debe pertenecer a una clase. Así
pues, el esqueleto de cualquier aplicación Java se basa en la definición de una clase.
Todos los datos básicos, como los enteros, se deben declarar en las clases
antes de hacer uso de ellos. En C la unidad fundamental son los archivos con código
fuente, en Java son las clases. De hecho son pocas las sentencias que se pueden
colocar fuera del bloque de una clase. La palabra claveimport (equivalente al
#include) puede colocarse al principio de un archivo, fuera del bloque de la clase.
Sin embargo, el compilador reemplazará esa sentencia con el contenido del archivo
que se indique, que consistirá, como es de suponer, en más clases.
3.2.9.1.Tipos de clases
Hasta ahora sólo se ha utilizado la palabra clave public para calificar el
nombre de las clases que se han visto, pero hay más modificadores. Los tipos de
clases que se pueden definir son:
-abstract
Una clase abstract tiene al menos un método abstracto. Una clase abstracta
no se instancia, sino que se utiliza como clase base para la herencia.
-final.
38
mailxmail - Cursos para compartir lo que sabes
Una clase final se declara como la clase que termina una cadena de herencia.
No se puede heredar de una clase final. Por ejemplo, la clase Math es una clase final.
-public.
Las clases public son accesibles desde otras clases, bien sea directamente o
por herencia. Son accesibles dentro del mismo paquete en el que se han declarado.
Para acceder desde otros paquetes, primero tienen que ser importadas.
-synchronizable.
Este modificador especifica que todos los métodos definidos en la clase son
sincronizados, es decir, que no se puede acceder al mismo tiempo a ellos desde
distintos hilos (t hreads) de programación; el sistema se encarga de colocar las
banderas (flags) necesarias para evitarlo. Este mecanismo hace que desde hilos
diferentes se puedan modificar las mismas variables sin que haya problemas de que
se sobrescriban.
-protected.
Una clase definida como protected puede ser utilizadas desde cualquier clase
del mismo paquete, sin posibilidad, a diferencia de las clases public, de ser
accedidas desde otros paquetes, aun importandolas.
3.2.9.2.Paquetes de clases
Al crear una clase se puede incluir en un paquete que se compilará como tal,
es decir, la aplicación no funcionará si falta alguna de las clases integrantes del
paquete.
La agrupación en paquetes es bastante útil para motivos de organización y
portabilidad, ya que nos obliga a tener todas las clases relacionadas a un proyecto
en la misma carpeta o directorio. Para incluir una clase en un paquete se hace de la
siguiente forma:
package Clases; //esta línea indica que esta clase forma parte del paquete
//clases
public MiClase {
int i;
....
3.2.10. Variables y métodos de instancia
39
mailxmail - Cursos para compartir lo que sabes
Una clase en Java puede contener variables y métodos. Las variables pueden
ser tipos primitivos como int, char, etc. Los métodos son funciones.
Por ejemplo, en el siguiente segmento de código podemos observarlo:
public MiClase {
int i;
public MiClase() {
i = 10;
}
public void Suma_a_i( int j ) {
i = i + j;
}
}
La clase MiClase contiene una variable (i) y dos métodos, MiClase que es el
constructor de la clase y Suma_a_i( int j ).
3.2.10.1.Ámbito de una variable
Los bloques de sentencias compuestas en Java se delimitan con dos llaves.
Las variables de Java sólo son válidas desde el punto donde están declaradas hasta
el final de la sentencia compuesta que la engloba. Se pueden anidar estas sentencias
compuestas, y cada una puede contener su propio conjunto de declaraciones de
variables locales. Sin embargo, no se puede declarar una variable con el mismo
nombre que una de ámbito exterior.
El siguiente ejemplo intenta declarar dos variables separadas con el mismo
nombre. En C y C++ son distintas, porque están declaradas dentro de ámbitos
diferentes. En Java, esto es ilegal.
Class ambito {
int i = 1;
{
// ámbito exterior
// crea un nuevo ámbito
40
mailxmail - Cursos para compartir lo que sabes
int i = 2;
// error de compilación
}
}
3.2.10.2.Métodos y Constructores
Los métodos son funciones que pueden ser llamadas dentro de la clase o por
otras clases. El constructor es un tipo específico de método que siempre tiene el
mismo nombre que la clase.
Cuando se declara una clase en Java, se pueden declarar uno o más
constructores opcionales que realizan la inicialización cuando se instancia (se crea)
un objeto de dicha clase.
Utilizando el código de ejemplo anterior, cuando se crea una nueva instancia
de MiClase, se crean (instancian) todos los métodos y variables, y se llama al
constructor de la clase:
MiClase mc;
mc = new MiClase();
La palabra clave new se usa para crear una instancia de la clase. Antes de ser
instanciada con new no consume memoria, simplemente es una declaración de tipo.
Después de ser instanciado un nuevo objeto mc, el valor de i en el objeto mc será
igual a 10. Se puede referenciar la variable (de instancia) i con el nombre del objeto:
mc.i++; // incrementa la instancia de i de mc
Al tener mc todas las variables y métodos de MiClase, se puede usar la
primera sintaxis para llamar al método Suma_a_i() utilizando el nuevo nombre de
clase mc:
mc.Suma_a_i( 10 );
y ahora la variable mc.i vale 21.
3.2.10.3.Finalizadores
Java no utiliza destructores (al contrario que C++) ya que tiene una forma de
recoger automáticamente todos los objetos que se salen del alcance. No obstante
proporciona un método que, cuando se especifique en el código de la clase, el
41
mailxmail - Cursos para compartir lo que sabes
reciclador de memoria (garbage collector) llamará:
// Cierra el canal cuando este objeto es reciclado
protected void finalize() {
close();
}
3.3. Programación multihilo (multithread)
Se le llama programación multihilo o multithreaded (en inglés) al método de
programación donde el flujo del programa se diseña como caminos o hilos
independientes que pueden ejecutarse al mismo tiempo,
Lo anterior significa que con Java se pueden realizar aplicaciones que realicen
varias tareas a la vez y que se puede controlar cada una de estas tareas
independientemente del resto.
La programación multihilo en Java se logra realizando una agrupación en
hilos como si de procedimientos se tratase, aunque se usa la clase thread.
En el siguiente ejemplo veremos como crear un hilo en lenguaje Java y como
ordenar su ejecución:
//La clase principal
public class MiClase{
//código de la clase
//Se deben instanciar las clases hilo.
Hilo1 H1;
Hilo2 H2;
H1 = new Hilo1 ("1");
H2 = new Hilo2 ();
//Llamadas a los hilos
H1.start
H2.start
42
mailxmail - Cursos para compartir lo que sabes
H2.start
}
//Hilos
class Hilo1 extends Thread { //Extiende (hereda) la clase Thread
//Constructor de la clase Hilo1
public Hilo1(String nom){ //este constructor utiliza un nombre de hilo
}
pulic void run(){
//run contiene el código que se ejecutará
}
}
class Hilo2 extends Thread {
public Hilo2(){ /*Variante del constructor que no pide nombre de hilo (define
los nombres de hilo como Thread1, Thread2... Treadn.*/
}
pulic void run(){
}
}
"Un programa inicia la ejecución de un hilo invocando el método start de ese
hilo, a su vez start, invoca el método r u n. Una vez que start echa a andar el hilo,
regresa de inmediato el control a su invocador. De ahí en adelante el invocador se
ejecutará en paralelo con el hilo iniciado. El método start lanza una excepción de
estado de hilo ilegal (IllegalThreadStateException) si el hilo que está tratando de
iniciar ya se está ejecutando."[1]
Desafortunadamente nuestro espacio para la explicación del lenguaje Java es
reducido puesto que no es ese nuestro tema principal, aunque se incluye por
considerarse necesario, y no podemos dedicarle lo merecido al interesante tema de
la programación multihilo. Recomendamos al lector el libro citado al pie de esta
43
mailxmail - Cursos para compartir lo que sabes
la programación multihilo. Recomendamos al lector el libro citado al pie de esta
página, dedica un capítulo (que tal vez resulte también insuficiente pero es mejor
que dos cuartillas) completo a este tema.
[1]Deitel y Deitel, Cómo programar en Java, pág. 673.
44
mailxmail - Cursos para compartir lo que sabes
4. ¿Qué es J2ME?
4. ¿QUÉ ES J2ME?
J2ME (Java 2 Micro Edition) es la plataforma basada en el lenguaje Java que
Sun Microsystems ha creado para la programación de dispositivos inalámbricos
pequeños como teléfonos celulares, paginadores y PDA. La figura 4.1 muestra como
está compuesta la plataforma J2ME.
Figura 4.1. Componentes de J2ME
4.1.Perfiles, configuraciones y máquinas virtuales
La edición micro de Java se compone, además del lenguaje, de una máquina
virtual, configuraciones, perfiles y paquetes adicionales (Fig. 4.1).
La máquina virtual es la base de la plataforma, es el interprete del lenguaje y
sobre la cual se han de ejecutar las aplicaciones, también sobre esta máquina virtual
corren las configuraciones (CDC y CLDC), las cuales incorporan apis básicas para la
creación de aplicaciones y sirven de soporte a los perfiles. Los perfiles incluyen la
mayor parte de las clases y apis que se van a utilizar en la programación, como
45
mailxmail - Cursos para compartir lo que sabes
pueden ser instrucciones de entrada y salida o de inicio y terminación de la
aplicación.
Los paquetes adicionales son aquellos que la especificación de la tecnología
inalámbrica Java (JSR185) no establece como obligatorios para incorporar en los
dispositivos. Ejemplos de esto pueden ser las apis de mensajes inalámbricos
(WMAPI) y de multimedia (WMAPI).
4.2. MIDP 2.0
Hasta este momento es el único perfil aplicado a los dispositivos en el
mercado, aunque se están investigando algunos otros, como el especializado en
PDA.
MIDP 2.0 incorpora apis de interfaz de usuario, de ciclo de vida del programa,
almacenamiento persistente, juegos, trabajo en red y multimedia. Según la
especificación de la tecnología inalámbrica de Java todo dispositivo que soporte
MIDP 2.0 debe incluir mínimamente las siguientes características:
-Debe permitir archivos Java (JAR) de más de 64 KB. y archivos descriptores
de aplicaciones (JAD) mayores a 5 KB.
-Se debe permitir a cada MIDlet la utilización de 30 KB de almacenamiento
persistente y se recomienda que las MIDlets incluyan información acerca de el
almacenamiento mínimo con el que trabajan correctamente.
-El espacio de memoria libre para una aplicación ejecutándose (Heap o del
montón) debe ser por lo menos de 256 KB.
-Soporte para pantallas de 125 x 125 pixeles, con una profundidad de color
de 12 bits.
-Se deben incluir la capacidad de que el dispositivo reaccione a eventos de
tiempo (una alarma a determinada hora, los llamados ticklers o despertadores).
-Mecanismos para tomar un número telefónico del directorio del equipo.
-Soporte para imágenes en formato JPEG y PNG.
-Acceso a contenidos multimedia por el protocolo HTTP 1.1.
4.3. CLDC 1.1
Además de la Configuración para Dispositivos Conectados (CDC), CLDC
46
mailxmail - Cursos para compartir lo que sabes
Además de la Configuración para Dispositivos Conectados (CDC), CLDC
(Connected Limited Device Configuration) es la única opción en configuraciones en
la tecnología inalámbrica Java, aunque esta última está dedicada a la clase de
aparatos que nos ocupan.
CLDC es la base para que los perfiles (como MIDP o PDAP) funcionen,
proveyendo las apis básicas y la máquina virtual (KVM). CLDC está diseñada para
equipos microprocesadores RISC o CISC de 16 a 32 bits y con una memoria mínima
de 160 KB para la pila de la tecnología Java.
La JSR185 pide como requisitos mínimos para todo equipo que implemente
CLDC 1.0 o 1.1
-Soporte mínimo para diez hilos relacionados con aplicaciones (MIDlets).
-Usar zonas de tiempo personalizables, con referencia en el formato de
zonas de tiempo GMT (GMT \7:00, por ejemplo).
-Soporte para propiedades de carácter y conversiones
mayúsculas-minúsculas en los bloques suplementales Unicode para Basic Latin y
Latin-1 (nuestros caracteres).
4.4. KVM
Como ya se explicó anteriormente la máquina virtual es la base de la
plataforma Java.
En el caso de la plataforma J2ME, debido a las capacidades limitadas de
almacenamiento, memoria, procesamiento y pantalla de los dispositivos; no se
puede integrar una máquina virtual Java (JVM) del las dimensiones de J2SE o J2EE.
Por esto se ha creado una nueva máquina virtual: KVM (Kilobyte Virtual Machine)
KVM es una máquina virtual Java compacta y portable específicamente
diseñada para ser la base de desarrollo en dispositivos pequeños y de recursos
limitados. Actualmente CLDC trabaja sobre KVM. Además KVM está diseñada para
mantener los aspectos centrales del lenguaje Java ejecutándose en unos cuantos
kilobytes de memoria (de ahí su nombre).
Aunque KVM deriva de la máquina virtual J2SE (JVM), algunas características
de esta última han sido eliminadas para soportar CLDC, debido a que resultan
demasiado costosas de implementar o su presencia supone problemas de seguridad,
47
mailxmail - Cursos para compartir lo que sabes
resultando una KVM con las siguientes limitantes:
-Soporte de punto flotante.- KVM no soporta números de punto flotante, esto
debido a que la mayoría de los dispositivos en los que se implementa no lo soportan
tampoco.
-Finalización.- Las apis CLDC no incluyen el método object.finalize, así que
no se pueden hacer operaciones de limpieza final antes de que el recolector de
basura tome los objetos.
-Manejo de errores.- CLDC sólo define tres clases error: java.lang.Error,
java.lang.OutOfMemoryError y java.lang.VirtualMachineError. Todo tipo de errores
que no sean en tiempo de ejecución se manejan de modo dependiente del
dispositivo, esto incluye la finalización de una aplicación o reinicio del dispositivo.
-Interfaz Nativa Java (JNI).-No se implementa JNI (posibilidad de incluir código
en C dentro de clases Java), primeramente por motivos de seguridad, aunque
también es considerado excesivo dadas las limitantes de memoria del dispositivo al
que se dirige.
-Cargadores de clases definidas por el usuario.- KVM debe tener un cargador
de clases que no pueda ser manipulado o remplazado por el usuario,
principalmente por razones de seguridad.
-No hay soporte para la API reflection. La API reflection es más bien usada
para aplicaciones de bajo nivel (depuradores, constructores GUI, etc). La falta de
esta API impide también la serialización de objetos.
-Grupos de hilos o hilos "demonio".- Aunque CLDC soporta programación
multihilo, no soporta grupos de hilos o hilos "demonio". Si requiere usar
operaciones para grupos de hilos use objetos de colección para almacenar los
objetos hilo a nivel de aplicación.
-Referencias débiles.- Ninguna aplicación construida con CLDC puede
requerir referencias débiles.
4.4.1.Verificador de clases
En la máquina virtual de J2SE, el verificador de clases es el responsable de
rechazar archivos de clase no válidos. Una máquina virtual que soporte CLDC (ahora
KVM) también debe ser capaz de rechazar estos archivos. Sin embargo, el proceso
48
mailxmail - Cursos para compartir lo que sabes
de verificación de clases es tardado y costoso y, por lo tanto, no recomendable para
equipos con recursos limitados.
Los diseñadores de KVM decidieron mover la mayor parte del trabajo de
verificación de clases fuera del dispositivo, es decir, hacia la computadora de
escritorio donde las clases son compiladas o el servidor de donde se descargan. A
este proceso de verificación se le llama preverificación. Los dispositivos son
únicamente responsables de ejecutar algunas pruebas en la clase preverificada para
asegurarse de que aún es válida.
4.5. Apis en J2ME-CLDC
Las apis para CLDC tienen el objetivo de proveer un conjunto de bibliotecas
mínimo y útil para el desarrollo de aplicaciones y definición de perfiles para una
gran variedad de aparatos.
Las apis CLDC se pueden dividir en dos categorías:
-Clases derivadas de apis J2SE. Estas se localizan en los paquetes java.lang,
java.io y java.util y se derivan de apis del Java Developement Kit (JDK) 1.3. En las
tablas 4.1 y 4.2 se muestra una lista detallada de las clases incluidas.
-Clases específicas para CLDC. Se localizan en el paquete javax.microedition y
sus subpaquetes y se explican con detalle abajo en la tabla 4.4.
4.5.1.Clases heredadas (derivadas)
CLDC hereda algunas clases de sistema, entrada y salida (E/S) y utilidades de
la plataforma J2SE, las tablas 4.1 y 4.2 muestran los paquetes y sus clases que se
heredaron de la edición estándar.
Tabla 4.1. Clases de NO excepción heredadas de J2SE
Paquete Clases
Boolean, Byte, Character, Class, Integer, Long, Math, Object,
java.lang Runnable, Runtime, Short, String, StringBuffer, System, Thread,
Throwable
java.io
ByteArrayInputStream, ByteArrayOutputStream, DataInput,
DataOutput, DataInputStream, DataOutputStream, InputStream,
OutputStream, InputStreamReader, OutputStreamWriter,
PrintStream, Reader, Writer
49
mailxmail - Cursos para compartir lo que sabes
java.util
Calendar, Date, Enumeration, Hashtable, Random, Stack,
TimeZone, Vector
Tabla 4.2. Clases de excepción heredadas de J2SE
Paquete Clases
ArithmeticException, ArrayIndexOutOfBoundException,
ArrayStoreException, ClassCastException,
ClassNotFoundException, Error, Exception, IllegalAccessException,
IllegalArgumentException, IllegalMonitorStateException,
java.lang IllegalThreadStateException, IndexOutOfBoundException,
InstantiationException, InterruptedException, OutOfMemoryError,
NegativeArraySizeException, NumberFormatException,
NullPointerException, RuntimeException, SecurityException,
StringIndexOutOfBoundException, VirtualMachineError
java.io
EOFException, IOException, InterruptedException,
UnsupportedEncodingException, UTFDataFormatException
java.util EmptyStackException, NoSuchElementException
4.5.1.1.Soporte de propiedades
En CLDC no hay implementación para la clase java.util.Properties, sin
embargo las propiedades mostradas en la tabla 4.3 están disponibles y se pueden
obtener llamando al método System.getProperty(clave), donde clave puede ser
cualquiera de las siguientes:
Tabla 4.3. Claves de las propiedades CLDC.
Valor
predeterminado
Clave
Explicación
microedition.platform
La plataforma o dispositivo
null
huésped.
microedition.encoding
Codificación
predeterminada de
caracteres.
ISO8859_1
microedition.configurations
Configuración y versión
J2ME actual.
CLDC-1.0
microedition.profiles
Nombre de los perfiles
soportados.
null
50
mailxmail - Cursos para compartir lo que sabes
4.5.2.Clases específicas de CLDC
Las siguientes clases (Tabla 4.4) son específicas de CLDC y están contenidas
en el paquete javax.microedition.io:
Tabla 4.4. Clases específicas de CLDC.
Paquete
Clases
Connection, ConnectionNotFoundException,
Connector, ContentConnector, Datagram,
javax.microedition.io DatagramConnection, InputConnection,
OutputConnection, StreamConnection,
StreamConnectionNotifier
4.5.2.1.Clases específicas de MIDP
Además de las clases específicas de MIDP contenidas en
javax.microedition.rms, javax.microedition.midlet y javax.microedition.lcdui; están
disponibles las siguientes clases, interfaces y clases de excepción:
- IllegalStateException. Clase en el paquete java.lang.
- Timer y TimerTask. Clases en el paquete java.util.
HttpConnection. Interfaz para acceso a una red por el protocolo HTTP
contenida en el paquete javax.microedition.io.
51
mailxmail - Cursos para compartir lo que sabes
5. Programación MIDP
5. PROGRAMACIÓN MIDP
En el presente capítulo se tratarán las características básicas de la
programación de aplicaciones MIDP, NO se trata de un tutorial de programación,
únicamente se revisarán los aspectos generales que hacen a la programación MIDP
diferente de el resto de las aplicaciones Java. En el siguiente capítulo se desarrollará
una aplicación y se explicará el funcionamiento de cada instrucción o bloque de
instrucciones.
Pero primero se deben conocer los pasos a seguir en el proceso de desarrollo
para facilitar su comprensión y dar una idea previa de lo que se deberá hacer.
5.1.Herramientas necesarias
A continuación se proporciona una lista con las herramientas necesarias para
programar MIDlets.
-Plataforma de desarrollo Java (J2SDK, por ejemplo) disponible en
java.sun.com.
-Editor de textos (block de notas, edit, vi, emacs, etc.).
-Compilador CLDC (1.0, 1.1) y al menos un perfil (MIDP, PDAP), ambos
disponibles en java.sun.com.
Hay algunos paquetes de desarrollo Java que incluyen los dos últimos
requisitos, además de proveer de otras herramientas que pueden facilitar la
programación.
-Java Wireless Toolkit: Este paquete se puede descargar del sitio Java de Sun
Microsystems, provee de un compilador (incluye CLDC y MIDP) y organizador de
proyectos. Se crea en este programa un proyecto y automáticamente se generan las
carpetas donde se han de guardar los códigos fuente, las clases, imágenes y otros
accesorios que la aplicación utilice. Disponible para Windows y Linux. En la figura
5.1 se muestra la barra de herramientas de JWT (Java Wireless Toolkit).
52
mailxmail - Cursos para compartir lo que sabes
Figura 5.1. Java Wireless Toolkit.
-Nokia Developer Suite for J2ME: En la sección de desarrolladores de Nokia
(www.nokia.com) se puede descargar este entorno de desarrollo, incluye todo lo
que se necesita para la programación de MIDlets (a excepción del SDK). Se puede
instalar en plataformas Windows 2000, XP y Linux.
Este paquete puede resultar poco conveniente para computadoras poco
actuales, ya que entre sus requerimientos se mencionan 256 MB de memoria RAM y
procesador de 300 Mhz.
-Motorola SDK for J2ME. Sólo existe para Windows, es otro kit de desarrollo
completo al que sólo hay que agregar el SDK de Java.
-Code Warrior. Quizá uno de los mejores entornos integrados de desarrollo
(IDE) para la edición micro de la plataforma Java. Se puede descargar gratis después
de suscribirse en el sitio de programadores de Motorola, aunque en versión de
evaluación por treinta días.
-Sun ONE Studio: Otro entorno completo, bastante útil, incluye editor de
texto, compilador, preverificador y simuladores para probar las aplicaciones. En el
sitio oficial de Sun se puede descargar una evaluación de 60 días o comprar la
versión completa.
En el presente documento se utilizará el Wireless Toolkit de Java, debido a
que es más pequeño (en espacio de disco) y, por lo tanto, más fácil de obtener con
una conexión telefónica a Internet, además de que está disponible para Linux que
será la plataforma empleada para la programación.
53
mailxmail - Cursos para compartir lo que sabes
5.2. Ciclo de desarrollo de una aplicación MIDP
Las aplicaciones MIDP (MIDlets) siguen un proceso desde que son ideadas
hasta que están instaladas y ejecutándose en los dispositivos. Dicho proceso es el
siguiente:
-Escritura.
-Compilación y preverificación.
-Prueba y corrección.
-Empaquetado.
-Prueba del paquete.
5.2.1. Escritura de la aplicación
Hay algunas reglas para el desarrollo de MIDlets, además de las establecidas
por el lenguaje, que deben ser consideradas:
-Toda MIDlet debe importar los paquetes javax.microedition.midlet.* y
javax.microedition.lcdui.*.
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
- javax.microedition.midlet.*. Define que se utilizará MIDP.
- javax.microedition.lcdui.*. Proporciona las apis de interfaz de usuario.
-Cada MIDlet debe extender la clase MIDlet.
public class FirstMIDlet extends MIDlet {
-Una MIDlet no debe tener ningún método public static void main(), contrario
a lo que sucede en las aplicaciones Java de escritorio.
Constructor de la clase
public FirstMIDlet() { }
Inicia la MIDlet, en lugar de main().
public void startApp() {
}
5.2.2. Compilar y preverificar
54
mailxmail - Cursos para compartir lo que sabes
Cuando se utiliza Java Wireless Toolkit, o algún otro entorno de desarrollo, el
proceso de compilación y preverificación es sumamente sencillo:
Una vez escrito el código de las clases que compongan la aplicación se
ejecuta JWT y se abre la aplicación a compilar. En JWT se puede también depurar el
código, probar y empaquetar la aplicación, etc.
Para compilar la MIDlet, una vez abierta, hay que seleccionar la opción "Build",
esto creará el archivo de clase o bien indicará los errores que se tengan. El
compilado con JWT incluye la preverificación.
También se puede compilar y preverificar el código fuente desde la linea de
comando del sistema operativo. Teniendo correctamente instalado el SDK de Java y
CLDC se utiliza:
javac -d .| bootclasspath [ruta] [archivo.java]
para crear el archivo de clase en el mismo directorio (-d .\) que el de código y
para preverificar el resultado de la compilación se utiliza el comando:
preverify -classpath [ruta_clases]
este comando preverifica todos los archivos de clases en el directorio
ruta_clases y guarda el resultado en un subdirectorio, dentro de esa ruta, llamado
"output", para preverificar una sola clase hay que especificar su nombre.
5.2.3. Probando la MIDlet
Si no se utiliza JWT la aplicación puede ser probada, desde linea de
comandos, con la siguiente instrucción:
m i d p [nombre_MIDlet]
Aunque si su aplicación consta de varias MIDlets o archivos de clase y requiere
dejar que el usuario escoja cuales ejecutar, entonces es necesario empaquetar la
aplicación.
Utilizando JWT este trabajo se simplifica bastante, sólo se abre un proyecto y,
si ya está compilado y preverificado, se selecciona "Run" o "Ejecutar" para ver el
resultado en un emulador (Fig. 5.2).
55
mailxmail - Cursos para compartir lo que sabes
Figura 5.2. Una de las vistas del emulador de JWT
5.2.4. Empaquetado
Si una aplicación consta de múltiples clases, una archivo .jar es usado para
agruparlas, así es más fácil de distribuir y desplegar. esto se puede hacer desde
linea de comandos de la siguiente manera:
jar cf [archivo.jar] [clases]
El siguiente paso es crear el archivo descriptor de la aplicación (archivo .jad),
56
mailxmail - Cursos para compartir lo que sabes
el cual provee información sobre el contenido del archivo .jar. Un ejemplo de
archivo descriptor de aplicación es el siguiente:
MIDlet-Name: MID1
MIDlet-Version:1.0.0
MIDlet-Vendor:Sun Microsystems, Inc.
MIDlet-Description: Primera MIDlet
MIDlet-Info-URL: http://java.sun.com/j2me.
MIDlet-Jar-URL: MID1.jar
MIDlet-Jar-Size: 1063
MicroEdition-Profile: MIDP-2.0
MicroEdition-Configuration: CLDC-1.0
MIDlet-1: MID,icono.png, MIDlet1
MIDlet-2: MID1,MIDlet2
Las primeras lineas resultarán, seguramente, más que obvias así que se
explicará únicamente el último elemento (MIDlet-1), este contiene los datos de la
primera MIDlet integrante del paquete (hay que recordar que un paquete se puede
componer de muchas MIDlets) y consta de tres partes: nombre de la aplicación,
ícono (opcional) y clase. El nombre aparecerá en la pantalla del dispositivo,
acompañado del ícono (si existe), para que pueda ser seleccionado y la clase es el
archivo compilado que se ejecuta al seleccionar un nombre.
5.2.5. Probar la aplicación empaquetada
Una vez que se ha empaquetado la aplicación, se puede probar para asegurar
su funcionamiento agregando la opción -descriptor al comando m i d p.
midp -descriptor MID1.jad
Utilizando en kit de Java es el mismo proceso que probar la aplicación sin
empaquetar, ya que al realizar el paquete se genera automáticamente el archivo
descriptor.
5.3. Mapeo de teclas
Se le llama mapeo de teclas a la distribución que tienen las funciones del
57
mailxmail - Cursos para compartir lo que sabes
dispositivo (navegación, comandos, selección, etc.) sobre las teclas exteriores, es
decir, la función desplazarse hacia la derecha, puede estar colocada en la tecla con
el número 6 de un teléfono y esa misma función puede operar sobre la tecla "é" de
otro dispositivo.
Cuando el programador requiere de aplicaciones altamente portables debe
darle a este aspecto una gran importancia, no se puede permitir que un comando o
función de su MIDlet quede inhabilitada a causa de un descuido de ese tipo.
5.4. Manejo de eventos
Cuando no se utilizan cajas de texto, listas o formularios en una pantalla, se
puede tomar el control de las acciones a realizar cuando se presione, libere o
mantenga presionada cierta tecla del dispositivo. A la presión (rápida o detenida) y
liberación de teclas se les conoce como eventos, aunque no son el único tipo de
eventos, también pueden serlo el cierre repentino y deliberado de la aplicación, la
llegada de un mensaje o una llamada telefónica, el encendido del teléfono, en fin,
un evento es toda acción llevada a cabo por un agente exterior a la MIDlet y que
influya sobre su operación.
Un evento es, por tanto, una acción sobre la MIDlet, tomada por el usuario en
tiempo de ejecución.
5.4.1.Llamadas
Se conoce como Llamada (Callback en inglés) a las invocaciones a métodos
definidos por el programador que son ejecutados por la aplicación en respuesta a
eventos.
Además de J2ME, los conceptos de llamadas y eventos son utilizados en
muchos entornos de programación, principalmente aquellos con desarrollo de
interfaces gráficas (Visual Basic, Delphi, Visual J, etc.).
En lenguajes como C++, las llamadas se implementan mediante apuntadores,
una función desvía su apuntador a otra al ocurrir un evento. Como Java no utiliza
apuntadores, las llamadas se implementan mediante interfaces, las interfaces son
clases que únicamente tienen métodos únicamente definidos, las acciones a tomar
en cada método dependen del programador, es decir, una interfaz es como el
esqueleto de una clase que debe ser llenado por el programador.
En MIDP hay cuatro clases de llamadas de interfaces de usuario.
-Comandos abstractos que son parte de la API de alto nivel.
58
mailxmail - Cursos para compartir lo que sabes
-Eventos de bajo nivel que representan presión y liberación de teclas.
-Llamadas al método paint() de una clase Canvas.
-Llamadas al método Run de objetos ejecutables (de la interfaz Runnable)
solicitados por una llamada al método CallSerially de la clase Dispaly.
5.4.2.Navegación en pantalla
Un aplicación MIDP, a excepción de las demasiado sencillas, siempre utilizará
más de una pantalla, por lo que resulta necesaria una herramienta que permita a los
usuarios desplazarse de una pantalla a otra según lo requiera la propia MIDlet.
El paquete javax.microedition.lcdui proporciona la clase Command, la cual
captura la información de un evento. Un comando creado con la clase Command
solamente contiene información sobre él mismo, no de la acción que realiza cuando
está activado, la acción está definida en un objeto de la interfaz CommandListener
asociado con la pantalla. A continuación se muestra como declarar un comando:
Command miComando=new Command("Mio", Command.SCREEN,2);
Los tres parámetros que el constructor de la clase Command debe incluir son
los siguientes:
-Etiqueta: Una cadena de caracteres usada para la presentación visual del
comando.
-Tipo: Especifica su intención, es decir, la acción que realizará el comando.
Algunos tipos pueden ser:
- Command.SCREEN.
- Command.EXIT.
- Command.BACK.
- Command.ITEM
-Prioridad: Describe la importancia del comando con respecto a otros en
pantalla. Una prioridad de 1 indica al comando más importante.
Cuando una MIDlet se ejecuta, el dispositivo escoge el lugar de un comando
basándose en su tipo (abajo y a la izquierda para salir, por ejemplo) y coloca los
comandos similares con base en su valor de prioridad. Considérese una aplicación
con los siguientes comandos:
59
mailxmail - Cursos para compartir lo que sabes
Command Salir=new Command("Exit", Command.EXIT,1);
Command miCom=new Command("Mio1", Command.SCREEN,2);
Command miCom2=new Command("Mio2", Command.SCREEN,2);
En este ejemplo, el manejador de la aplicación coloca el comando Exit en la
pantalla y crea un menú para mantener Info y Buy. Presionando la tecla
correspondiente al menú, el usuario, es llevado a otra pantalla donde puede
seleccionar alguna de las dos opciones. (Fig. 5.3)
La clase Command provee de tres métodos para conocer tipo, etiqueta y
prioridad del comando.
-int getCommandType().
-String getLabel().
-int getPriority().
Figura 5.3. Ejemplo de la clase Command.
5.4.3. Eventos de alto nivel
El manejo de eventos en la api de alto nivel se basa en un modelo de escucha
o escuchante (listener, en inglés). Los objetos Screen y Canvas pueden tener
"escuchantes" para comandos. Para que un objeto pueda ser un escuchante, debe
implementar la interfaz CommandListener.
Los comandos se agregan a un objeto con el método addCommand y se
registra un escuchante con el método CommandListener. Ambos métodos
pertenecen a la clase Displayable y son heredados por Screen y Canvas.
5.4.3.1.La interfaz CommandListener
Esta interfaz es para MIDlets que necesitan recibir eventos de alto nivel. Esta
interfaz tiene un método que todo escuchante debe implementar, el método CommanAction
60
mailxmail - Cursos para compartir lo que sabes
CommanAction.
public void CommanAction(Command c, Displayable d) { }
El parámetro c es un objeto Command que identifica al comando que fue
agregado al d (objeto Displayable) con el método addCommand. El parámetro d es
el objeto visible (Form, Canvas, Screen) donde el evento ocurre. Dentro de las llaves
({ }) se introduce el código que se habrá de ejecutar al ocurrir un evento.
public void commandAction(Command c, Displayable s) {
if (c == CmdSalir) {
destroyApp(false);
notifyDestroyed();
}
En el trozo de código mostrado arriba se emplea una decisión i f para
identificar qué comando es el que ha sido activado, si se trata del comando CmdSalir
se destruye la aplicación, es decir, se cierra y se notifica su destrucción.
5.4.3.2.La interfaz ItemStateListener
Las aplicaciones usan esta interfaz para recibir eventos que indican cambios
en el estado interno de los elementos en una pantalla, formulario. Esto pasa cuando
el usuario hace cualquiera de las siguientes cosas.
-Ajustar el valor de una barra de avance interactiva.
-Ingresar o modificar el valor de un TextField.
-Ingresar una nueva fecha u hora en una DateField.
-Cambiar los valores seleccionados en un ChoiceGroup.
Esta interfaz tiene un único método que un escuchante debe implementar.
public void itemStateChanged(Item i){ }
Para registrar un escuchante de esta interfaz se utiliza el método setItemStateListener
setItemStateListener.
ItemStateListener listener = new ItemStateListener();
Form.setItemStateListener(listener);
61
mailxmail - Cursos para compartir lo que sabes
public void itemStateChanged(Item item) {
}
5.4.4.Eventos de bajo nivel
Si se usa la clase Canvas para tener acceso a eventos de entrada o para
dibujar gráficos en la pantalla, es necesario manejar los eventos de bajo nivel.
Aplicaciones como los juegos, pueden ser realizadas utilizando la clase Canvas,
gracias a que esta posee métodos para controlar eventos de teclas y los llamados
"eventos de juego". Los eventos de teclas se registran como códigos específicos que
están directamente relacionados a las teclas del aparato. Los eventos de juego
producen un código relacionado con la acción asignada a una tecla del dispositivo.
5.4.4.1.Eventos de teclas
Como ya se mencionó, los eventos de teclas están relacionados directamente
con las teclas del dispositivo. Los eventos de teclas definidos en la clase Canvas son:
-KEY_NUM0.
-KEY_NUM1.
-KEY_NUM2.
-KEY_NUM3.
-KEY_NUM4.
-KEY_NUM5.
-KEY_NUM6.
-KEY_NUM7.
-KEY_NUM8.
-KEY_NUM9.
-KEY_STAR.
-KEY_POUND.
Los números del 0 al 9, el asterisco "*" y la almohadilla "#" son las teclas
genéricas en todos los dispositivos, quizá existan otros eventos en diferentes
dispositivos, pero por motivos de portabilidad, se recomienda solamente usar estos.
5.4.4.2.Eventos de juego
62
mailxmail - Cursos para compartir lo que sabes
5.4.4.2.Eventos de juego
Debido a que en los equipos actuales varía mucho la distribución de las teclas,
si se requiere una localización específica de estas, flechas de dirección, por ejemplo;
es necesario utilizar los eventos de juego, estos eventos son los siguientes:
-DOWN.
-LEFT.
-RIGHT.
-UP.
-FIRE.
-GAME_A.
-GAME_B.
-GAME_C.
-GAME_D.
Anteriormente se explicó que cada aparato mapea sus teclas de forma
diferente al resto. Con los eventos de juego se puede lograr que la aplicación
reaccione a la presión de una tecla que indique movimiento hacia arriba en el
dispositivo anfitrión, independientemente del código de esta tecla.
5.4.5.Métodos de manejo de eventos
Para el manejo de eventos de bajo nivel, J2ME provee al programador de los
siguientes métodos:
- protected void keyPressed (int keyCode).
- protected void keyReleased (int keyCode)
- protected void keyRepeated (int keyCode)
- protected void pointerPressed (int x, int y)
- protected void pointerDragged (int x, int y)
- protected void pointerReleased (int x, int y)
- protected void showNotify ()
- protected void hideNotify ()
63
mailxmail - Cursos para compartir lo que sabes
- protected void hideNotify ()
- protected abstract void paint (Graphics g)
- commandAction(). De la interfaz CommandListener.
Cabe aclarar que los eventos de apuntador (pointer) no son aplicables a todos
los dispositivos, sólo para aquellos que cuenten con un indicador de ese tipo
(apuntador de ratón, por ejemplo), se puede comprobar si el equipo lo soporta
utilizado los métodos hasPointerEvents y hasPointerMotionEvents. Otro método que
podría no estar disponible en algún dispositivo es keyRepeated, se puede
comprobar su operabilidad con el método hasRepeatEvents.
5.5. Principales componentes
Son muchos y muy variados los componentes que incluyen las apis de MIDP y
CLDC, además cada programador puede crear componentes propios tomando como
base los ya existentes.
Por lo anterior, se explicarán sólo aquellos que son considerados cómo
básicos para la realización de aplicaciones en general.
En la tabla 5.1 se muestra cada uno de los componentes y una breve
descripción de estos.
Tabla 5.1. Clases del paquete lcdui.
Clase
Descripción
Alert
Pantalla que muestra un mensaje al usuario y espera un cierto
periodo de tiempo antes de pasar a la siguiente pantalla.
AlertType
Indica la naturaleza de las alertas (exclamación, error, etc.).
Canvas
Es la base para la escritura de aplicaciones que utilicen eventos de
bajo nivel y para dibujar objetos gráficos en la pantalla.
ChoiceGroup
Un grupo de elementos seleccionables, colocados dentro de una
forma (Form).
Command
Una estructura que encapsula la información semántica de una
acción (p. ej. "Salir").
DateField
Componente modificable para presentar en pantalla la información
de hora y fecha.
Display
Representa el manejador de pantalla y dispositivos de entrada
(teclas) del sistema.
Displayable
Característica de un objeto que tiene la capacidad de ser mostrado
en la pantalla.
64
mailxmail - Cursos para compartir lo que sabes
Font
Representa las propiedades de la fuente (letra).
Form
Una pantalla que contiene una mezcla arbitraria de elementos
(imágenes, cajas de texto, etc.)
Gauge
Una barra que puede ser utilizada como indicador de avance en un
proceso.
Graphics
Provee la capacidad de presentar gráficos 2D simples.
Image
Muestra una imagen.
ImageItem
Proporciona el control de trazado (colocación) de una imagen en una
forma o alerta
Item
Superclase para elementos que van a ser adheridos a una forma o
alerta.
List
Muestra una lista de opciones.
Screen
La superclase de todas las clases de interfaz de usuario de alto nivel.
StringItem
Un elemento que puede contener una cadena de caracteres.
TextBox
Pantalla que permite al usuario insertar y modificar texto.
TextField
Un componente de texto modificable que puede ser colocado en una
forma.
Ticker
Un texto que cruza continuamente la pantalla, puede ser agregado
en todos los tipos de pantalla a excepción de canvas.
En los siguientes apartados se verá como utilizar algunas de las clases
listadas en la tabla anterior, esto mediante la explicación de programas sencillos
que, aunque pudiesen carecer de utilidad, ilustran de forma clara la forma de
implementar una clase determinada.
5.5.1.¡Hola mundo!
Se iniciará esta serie de programas de ejemplo con el clásico código fuente de
una aplicación que lo único que hace es mostrar un mensaje en la pantalla, en este
caso el mensaje es "¡Hola mundo!".
El código fuente es el siguiente:
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
Se comienza por importar los paquetes que son obligatorios para cualquier
MIDlet. Esto puede variar dependiendo de la finalidad de la clase a crear, pero para
una clase que será la principal de su paquete, es forzoso el importar las clases del
paquete javax.microedition.midlet y lcdui se requiere para las operaciones de
entrada y salida de información.
65
mailxmail - Cursos para compartir lo que sabes
public class Holamundo extends MIDlet implements CommandListener {
Se inicia el código de la clase, la cual extiende (o hereda) la clase MIDlet,
necesaria, como ya se dijo, para la creación de clases ejecutables, además se
implementa la interfaz CommandListener, esta interfaz define una estructura de
clase en la que se deben declarar unos objetos llamados comandos, los cuales
responderán a alguna acción del usuario.
private Form Forma1;
Se ha instanciado un objeto de la clase Form, que, como ya se vio, pertenece
al paquete lcdui. Un objeto Form define una pantalla donde se agregarán objetos de
entrada y salida de datos por teclado y pantalla, respectivamente.
public Holamundo() {
Forma1 = new Form("¡Hola mundo!");
Forma1.append(new StringItem(null, "¡Hola mundo!"));
Forma1.addCommand(new Command("Salir", Command.EXIT, 1));
Forma1.setCommandListener(this);
}
En la sección de arriba se describe el constructor de la clase, el cual debe
llevar el mismo nombre que la clase, sin especificar tipo y de acceso público. En un
constructor de clase se establecen los valores que han de tomar las variables, se
crean las instancias de las clases, en fin, se define como habrá de presentarse, al
arrancar, la aplicación al usuario. Al iniciar el constructor se instancia un objetoForm
con título "¡Hola mundo!" y de nombre Forma1, enseguida, a esa misma forma se le
agrega un elemento SringItem sin etiqueta (título o descripción) y con un contenido
igual a "¡Hola mundo!", se agrega también, un comando, los comandos son las
opciones que, en un teléfono celular, aparecen en la parte inferior de la pantalla y
que el usuario puede seleccionar presionando las teclas que se encuentran debajo
del texto; ese comando tiene por texto o etiqueta la palabra "Salir", es del tipo EXIT
y de prioridad 0. En un comando, la prioridad indica la importancia de ese comando
con respecto a otros en la pantalla, dicha prioridad puede ir de 1 a 10, siendo 1 el
valor de prioridad más alto.
public void startApp() {
Display.getDisplay(this).setCurrent(Forma1);
66
mailxmail - Cursos para compartir lo que sabes
Display.getDisplay(this).setCurrent(Forma1);
}
El método startApp es el que se ejecuta después del constructor, en el se
encuentran las instrucciones que serán ejecutadas por la aplicación (es como el
procedimiento main de las aplicaciones en C o Java). En el ejemplo, se utiliza un
objeto Display que se encarga del control de la pantalla, un método getDisplay(this),
el cual arroja como resultado la pantalla del dispositivo (como salida de datos para
la MIDlet this, la actual) y otro método setCurrent(Forma1), que indica que lo que se
ha de mostrar en la pantalla será lo contenido en Forma1.
public void pauseApp() {}
public void destroyApp(boolean unconditional) {}
public void commandAction(Command c, Displayable s) {
notifyDestroyed();
}
}
Las funciones pauseApp y destroyApp, también son obligatorias de cualquier
clase MIDP (sea la principal del proyecto o no), en pauseApp se pueden definir las
acciones a tomar cuando la aplicación sea pausada por el usuario, por la misma
aplicación o por otra; destroyApp indica lo que se realizará antes de cerrar la
aplicación. El método commandAction define lo que se hará con los comandos de la
MIDlet, como en el ejemplo sólo se tiene un comando basta con notificar la
destrucción de la aplicación,
5.5.2.TextBox
La clase TextBox, genera objetos de pantalla completa, como Form,la
diferencia es que TextBox únicamente permite la introducción de texto y el manejo
67
mailxmail - Cursos para compartir lo que sabes
de comandos. A continuación se presenta un ejemplo sencillo:
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class TextBoxDemo extends MIDlet
implements CommandListener {
private Command CmdSalir;
private Display display;
private TextBox t = null;
De esta primera parte sólo hay que resaltar la creación de un objeto TextBox
de nombre t y valor nulo.
public TextBoxDemo() {
display = Display.getDisplay(this);
CmdSalir = new Command("Salir", Command.EXIT, 2);
t = new TextBox("Hola", "Esto es una prueba", 256, 0);
t.addCommand(CmdSalir);
t.setCommandListener(this);
}
En el constructor de la MIDlet se observa la designación del contenido de la
pantalla (esta MIDlet), la creación de un comando de salida de prioridad 2, la
instanciación de t con título "Hola", texto inicial "Esto es una prueba", longitud
máxima de texto de 256 caracteres y tipo 0, los tipos son valores constantes que la
clase TextBox interpreta para permitir la entrada de algunos caracteres e impedir la
de otros, en el ejemplo el tipo 0 es la constante Textfield.ANY, que indica que se
puede introducir cualquier carácter (también existen Textfield.NUMERIC,
68
mailxmail - Cursos para compartir lo que sabes
TextField.DECIMAL y TextField.PHONENUMBER, entre otras). Después de creado el
objeto t, se le agrega el comando CmdSalir.
public void startApp() {
display.setCurrent(t);
}
En el método startApp se indica que el elemento en pantalla será t.
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
}
public void commandAction(Command c, Displayable s) {
if (c == CmdSalir) {
destroyApp(false);
notifyDestroyed();
}
}
}
Cuando una MIDlet tiene más de un comando (aunque no es el caso de esta)
se puede realizar una decisión con el parámetro c para definir las acciones a tomar
en cada comando.
5.5.3.TextField
69
mailxmail - Cursos para compartir lo que sabes
El elemento Textfield trabaja sobre una forma (Form), por lo que ésta debe
ser declarada e instanciada antes de utilizar el Textfield.
private Form mainForm;
mainForm = new Form("Text Field");
private TextField txt1;
txt1=newTextField("Cualquier carácter", "", 15, TextField.ANY)
Una vez instanciada la forma, se utiliza el método append para crear en ella
los campos de texto que consideremos necesarios:
mainForm.append(txt1);
mainForm.append(new TextField("E-Mail", "", 15, TextField.EMAILADDR));
mainForm.append(new TextField("Entero", "", 15, TextField.NUMERIC));
mainForm.append(new TextField("Decimal", "10.5", 15, TextField.DECIMAL));
mainForm.append(new TextField("Teléfono", "", 15,
TextField.PHONENUMBER));
mainForm.append(new TextField("Password", "", 15, TextField.PASSWORD));
mainForm.append(new TextField("URL", "", 15, TextField.URL));
Como se puede observar, el constructor de la clase Textfield lleva cuatro
parámetros, el primero de ellos indica la etiqueta que habrá de llevar el campo de
texto, en el segundo se debe especificar el valor inicial, el siguiente es la longitud
(en caracteres) que habrá de tolerar el campo y el último indica el filtro a utilizar
para dicho campo, es decir, que caracteres admitirá.
Quizá el lector observe que, el utilizar la palabra clave new dentro de los
70
mailxmail - Cursos para compartir lo que sabes
paréntesis del método append podría limitar el control que se tenga sobre los
objetos de la forma, por lo tanto es recomendable declarar e instanciar antes los
objetos y sólo nombrarlos en append, como se hizo con t x t 1.
Hay varios filtros además de los mostrados en el ejemplo, se recomienda al
lector interesado revisar la especificación de la clase Textfield.
5.5.4.Datefield
Hasta ahora no se ha descrito, en las clases TextField y TextBox, un método
relativamente fácil para introducir una fecha, los signos "/" en un teléfono son un
poco difíciles de localizar, sin contar con que los números son la última o una de las
últimas opciones en el orden de las teclas de la mayoría de los equipos, estos
problemas no lo son tanto si se utiliza la clase DateField. Enseguida se explica cómo.
public class DateFieldDemo extends MIDlet implements CommandListener{
private final static Command CmdSalir = new Command("Salir",
Command.EXIT,1);
private boolean primera;
private Form mainForm;
public DateFieldDemo() {
Primera = true;
Forma = new Form("Ejemplo de fecha");
}
En esta clase de nombre DateFieldDemo, se utilizan por primera vez los
cambios de pantalla, implícitos en la clase DateField, es por esto que se crea una
variable lógica Primera que servirá para no instanciar los objetos dos veces (al
iniciar la MIDlet y al volver del cambio de pantalla).
71
mailxmail - Cursos para compartir lo que sabes
protected void startApp() {
if(Primera) {
Forma.append(new DateField("Fecha", DateField.DATE));
Forma.append(new DateField("Hora", DateField.TIME));
Forma.append(new DateField("Fecha y hora",
DateField.DATE_TIME));
Forma.addCommand(CmdSalir);
Forma.setCommandListener(this);
Primera = false;
}
Display.getDisplay(this).setCurrent(mainForm);
}
Además de los ya conocido, en startApp se agregan a la forma tres campos de
fecha, el primero pedirá, según la forma que tenga dispositivo de hacerlo, la fecha;
el segundo pedirá la hora y el tercero ambos datos.
protected void destroyApp(boolean unconditional) { }
protected void pauseApp() { }
public void commandAction(Command c, Displayable d) {
if (c == CmdSalir) {
destroyApp(false);
notifyDestroyed();
}
}
72
mailxmail - Cursos para compartir lo que sabes
}
}
5.5.5.StringItem
Cuando lo que se desea no es pedir al usuario un dato, sino mostrarle
información, seguramente no es recomendable utilizar clases cuyo contenido pueda
ser alterado. Para esto se utiliza la claseStringItem.
public class StringItemDemo extends MIDlet implements CommandListener,
ItemCommandListener
{
private Display display;
private Form mainForm;
private final static Command IR = new Command("Ir", Command.ITEM, 1);
private final static Command PRES = new Command("Pres",
Command.ITEM, 1);
private final static Command Salir = new Command("Salir",
Command.EXIT, 1);
En esta primera parte del código de la clase, se puede ver que se ha
implementado una nueva interfaz, ItemCommmandListener, la cual se utiliza para
agregar comandos, no sólo a los objetos de pantalla completa (como TextBox o
Form), sino a los agrupados en estos objetos. Ahora podemos utilizar los comandos
de tipo ITEM.
protected void startApp() {
73
mailxmail - Cursos para compartir lo que sabes
protected void startApp() {
display = Display.getDisplay(this);
mainForm = new Form("String Item Demo");
mainForm.append("Esto es sólo una etiqueta");
StringItem item = new StringItem("Etiqueta de String Item: ",
"Texto de String Item");
mainForm.append(item);
item = new StringItem("Etiqueta: ", "texto");
mainForm.append(item);
item = new StringItem("Hipervínculo ", "Ir a", Item.HYPERLINK);
item.setDefaultCommand(IR);
item.setItemCommandListener(this);
mainForm.append(item);
item = new StringItem("Botón ", "Presionar", Item.BUTTON);
item.setDefaultCommand(PRES);
item.setItemCommandListener(this);
mainForm.append(item);
mainForm.addCommand(Salir);
mainForm.setCommandListener(this);
display.setCurrent(mainForm);
}
En startApp sólo se muestran las posibilidades que se tienen para escribir un
texto que el usuario no pueda modificar y que además pueda ejecutar alguna
acción. Desde simplemente escribir "Esto es sólo una etiqueta" hasta un botón que,
al ser presionado, puede realizar una tarea. En el ejemplo, se distinguen dos tipos
74
mailxmail - Cursos para compartir lo que sabes
al ser presionado, puede realizar una tarea. En el ejemplo, se distinguen dos tipos
de constructores de StringItem, el primero, que tiene como parámetros dos cadenas
de texto (etiqueta o título y contenido) y el segundo, que además permite la
definición de un tipo de texto (hipervínculo o botón).
public void commandAction(Command c, Item item) {
if (c == IR) {
String text = "Ir a la URL...";
Alert a = new Alert("URL", text, null, AlertType.INFO);
display.setCurrent(a);
} else if (c == PRES) {
String text = "Do an action...";
Alert a = new Alert("Action", text, null, AlertType.INFO);
display.setCurrent(a);
}
}
public void commandAction(Command c, Displayable d) {
destroyApp(false);
notifyDestroyed();
}
Tal vez llame la atención el hecho de tener dos funciones con el mismo
nombre (CommandAction), pero hay que observar los parámetros que toman al
activarse, ambas capturan un evento en el teclado, pero la primera captura los
eventos ocasionados en el componente de la forma que esté seleccionado, mientras
el otro se ocupa de los eventos generales y funcionan solamente sobre los eventos
75
mailxmail - Cursos para compartir lo que sabes
protected void destroyApp(boolean unconditional) {
protected void pauseApp() {
}
}
}
5.5.6.ChoiceGroup
Los grupos de selección o ChoiceGroup, por su nombre en inglés, son listados
de elementos en los que se puede seleccionar uno o varios de ellos y permiten
realizar operaciones (principalmente comparaciones lógicas) con dicha selección.
import javax.microedition.lcdui.*;
import javax.microedition.midlet.MIDlet;
public class Choice1 extends MIDlet implements CommandListener {
private final static Command CMD_Salir = new Command("Salir",
Command.EXIT,1);
private Display display;
private boolean primera;
private Form Forma;
public ChoiceGroupDemo() {
primera = true;
}
Hasta este punto todo se ve como en los ejemplos anteriores, se importan las
clases, se define la clase con extensiones e implementaciones, se crea un comando
76
mailxmail - Cursos para compartir lo que sabes
clases, se define la clase con extensiones e implementaciones, se crea un comando
que se utilizará para salir de la MIDlet, se declaran los objetos y variables a utilizar y
se determina lo que ha de hacer el constructor.
protected void startApp() {
if(primera) {
display = Display.getDisplay(this);
Forma = new Form("Choice Group");
Forma.append("Choice Group");
Image[] ArrImg = null;
try {
Image Imag = Image.createImage("/midp/uidemo/icono.png");
ArrImg = new Image[] { Imag, Imag, Imag, Imag };
} catch (java.io.IOException err) {
}
String[] arrString = {
"Opcion A", "Opcion B", "Opcion C", "Opcion D"
};
ChoiceGroup[] grupos = {
new ChoiceGroup("Exclusivo", ChoiceGroup.EXCLUSIVE,
arrString,ArrImg),
new ChoiceGroup("Multiple", ChoiceGroup.MULTIPLE,
arrString,ArrImg),
new ChoiceGroup("Pop-Up", ChoiceGroup.POPUP,
77
mailxmail - Cursos para compartir lo que sabes
arrString,ArrImg)
};
for (int iter = 0; iter < grupos.length; iter++) {
Forma.append(grupos[iter]);
}
Forma.addCommand(CMD_Salir);
Forma.setCommandListener(this);
primera = false;
}
display.setCurrent(Forma);
}
startApp crea tres grupos de selección, exclusivo, múltiple y pup-up. Todos
estos grupos contienen cuatro opciones (A, B, C y D) y cuatro iconos,
correspondientes, cada uno, a una opción, contenidos en arrString y arrImg,
respectivamente.
El grupo exclusivo es aquel que permite que uno y sólo uno de sus elementos
esté seleccionado. El grupo múltiple permite la selección de uno o varios elementos.
El modo pop-up permite escoger una sola opción, pero, a diferencia del exclusivo,
las muestra en un menú desplegable.
public void commandAction(Command c, Displayable d) {
if (c == CMD_Salir) {
destroyApp(false);
notifyDestroyed();
}
}
78
mailxmail - Cursos para compartir lo que sabes
}
protected void destroyApp(boolean unconditional) {
}
protected void pauseApp() {
}
}
5.5.7.List
Una lista de elementos, a diferencia de lenguajes como HTML WML, en J2ME
es una lista de la que se puede seleccionar un elemento, es decir, es como un grupo
de selección (ChoiceGroup). A continuación se muestra un código de ejemplo para la
utilización de listas:
public class Lista extends MIDlet implements CommandListener {
private final static Command SALIR = new Command("Salir",
Command.EXIT, 1);
private final static Command ATRAS = new Command("Atrás",
Command.BACK, 1);
private Display display;
private List Lista1;
private List ListaEx;
private List ListaIm;
private List ListaMul;
private boolean primera;
79
mailxmail - Cursos para compartir lo que sabes
En esta primera parte del código únicamente se han declarado las variables y
los objetos que se utilizarán, notese que entre esos objetos se encuentran cuatro
listas u objetos List.
public Lista() {
display = Display.getDisplay(this);
String[] Opciones = {"Opción A", "Opción B", "Opción C", "Opción D"
};
Image[] imagenes = null;
ListaEx = new List("Exclusiva", Choice.EXCLUSIVE, Opciones,
imagenes);
ListaEx.addCommand(ATRAS);
ListaEx.addCommand(SALIR);
ListaEx.setCommandListener(this);
ListaIm = new List("Implícita", Choice.IMPLICIT, Opciones,
imagenes);
ListaIm.addCommand(ATRAS);
ListaIm.addCommand(SALIR);
ListaIm.setCommandListener(this);
ListaMul = new List("Múltiple", Choice.MULTIPLE, Opciones,
imagenes);
ListaMul.addCommand(ATRAS);
ListaMul.addCommand(SALIR);
ListaMul.setCommandListener(this);
80
mailxmail - Cursos para compartir lo que sabes
primera = true;
}
El constructor de la clase principal crea un arreglo Opciones que contiene los
elementos de la lista, otro arreglo imagenes de valor nulo que definirá las imágenes
a utilizar como iconos a mostrar al lado de los elementos; finalmente se instancian
las listas según el tipo al que pertenecerán y se les agrega un comando ATRAS y otro
SALIR.
protected void startApp() {
if(primera) {
Image[] imagenes = null;
try {
Image icono = Image.createImage("/GNG/reporte/Icono.png");
imagenes = new Image[] {icono, icono, icono
};
} catch (java.io.IOException err) {
}
String[] Opciones = {"Exclusiva", "Implícita", "Múltiple"
};
Lista1 = new List("Seleccione tipo", Choice.IMPLICIT, Opciones,
imagenes);
Lista1.addCommand(SALIR);
Lista1.setCommandListener(this);
display.setCurrent(Lista1);
primera = false;
}
81
mailxmail - Cursos para compartir lo que sabes
}
Al iniciar la aplicación, se llena el arreglo de imágenes con un archivo
(icono.png) que se encuentra en el disco duro de la computadora (esa imagen se
incluirá en el paquete que se ha de instalar en el dispositivo final). Se hace esto
dentro de un bloque try-catch para evitar que la MIDlet se colapse en caso de no
encontrar las imágenes. Después se instancia la lista Lista1, se le agrega un
comando SALIR y se designa como actualmente activa en el display, esto es, en la
pantalla del dispositivo.
protected void destroyApp(boolean unconditional) {
}
protected void pauseApp() {
}
public void commandAction(Command c, Displayable d) {
if (d.equals(Lista1)) {
if (c == List.SELECT_COMMAND) {
if (d.equals(Lista1)) {
switch (((List)d).getSelectedIndex()) {
case 0:
display.setCurrent(ListaEx); break;
case 1:
display.setCurrent(ListaIm); break;
case 2:
display.setCurrent(ListaMul); break;
}
}
82
mailxmail - Cursos para compartir lo que sabes
}
} else {
// in one of the sub-lists
if (c == ATRAS) {
display.setCurrent(Lista1);
}
}
if (c == SALIR) {
destroyApp(false);
notifyDestroyed();
}
}
}
Al presionarse cualquier botón de comando en el dispositivo, la MIDlet entra
en un proceso de selección donde, dependiendo de la opción activa de Lista1 se
mostrará en pantalla una u otra de las listas restantes.
5.5.8.Alert
En ocasiones puede ser necesario mostrar en el dispositivo, algún mensaje de
error, advertencia o simple información. Hacer esto con un objeto StringItem
resultaría mucho más difícil que productivo. Para esta tarea, MIDP incluye una clase
llamada Alert, la cual se explica enseguida:
Alert alerta = new Alert("Alerta");
alert.setType(TipodeAlerta);
83
mailxmail - Cursos para compartir lo que sabes
alert.setTimeout(Tiempo);
alert.setString("Texto de la alerta");
display.setCurrent(alert);
A diferencia de las clases anteriores, a partir de ésta, sólo se mostrará la parte
fundamental del funcionamiento de la clase, esto debido a que el lector ya se habrá
familiarizado con el resto del método de programación J2ME (declaración de
variables, funciones, etc.).
En las primeras líneas de código se muestra, primero, la declaración en
instanciación de un objeto Alert, segundo, la definición de sus propiedades tipo,
tiempo y texto; y, tercero, la forma de activar la alerta (ponerla en pantalla).
Los tipos que una alerta permite son los siguientes:
-AlertType.ALARM.
-AlertType.CONFIRMATION.
-AlertType.ERROR.
-AlertType.INFO.
-AlertType.WARNING.
Todos los tipos de alerta agregan al mensaje escrito un timbre o vibración
que depende de las capacidades y configuración del dispositivo en que se ejecute la
MIDlet, por ejemplo, AlertType.ALARM hará que un teléfono vibre o timbre con el
mismo tono configurado en su despertador.
En cuanto a los tiempos, estos se dan en segundos:
84
mailxmail - Cursos para compartir lo que sabes
int Tiempo = 2 * SECOND;
int Tiempo=4 * SECOND;
int Tiempo=7 * SECOND;
O bien, puede no estar definido y esperar respuesta del usuario:
int Tiempo=Alert.FOREVER;
5.5.9.Gauge
Gauge es, como la traducción al español lo dirá, un medidor de avance,
similar a las barras de progreso (progress bar) de sistemas como Windows.
Aunque se podría pensar que su objetivo es meramente estético, es decir,
fuera de la función de contador (que puede serlo cualquier variable entera) una
barra de avance solamente sirve para informar gráficamente al usuario del progreso
de determinada operación. En J2ME, hay un tipo de barra de avance llamado interactivo
interactivo, este tipo interactivo es similar al control de volumen de los teléfonos
celulares, en los cuales el usuario puede seleccionar el nivel de volumen que desea.
Ahora ya no es meramente estético.
El funcionamiento de Gauge se describe a continuación:
Gauge G1=new Gauge("Interactiva", true, 20,0);
mainForm.append(G1);
Gauge G2=new Gauge("No interactiva", false, 20,0);
mainForm.append(G2);
85
mailxmail - Cursos para compartir lo que sabes
Donde el primer parámetro representa el título o etiqueta que el contador
llevará, el segundo, un valor lógico, indica si se permitirá al usuario alterar el valor
de avance (interactivo o no interactivo), el tercero define el número total de
unidades que habrá de contener la barra y el último el valor inicial, en las mismas
unidades, con que se mostrará.
5.6. Mejorando las aplicaciones
Se puede añadir calidad de presentación y funcionalidad a las MIDlets
dándoles la capacidad de enviar mensajes, agregándoles iconos, textos móviles o
contenido multimedia
5.6.1. Teletipos
Los teletipos son textos que se desplazan en la parte superior de la pantalla
del dispositivo. Para crear un teletipo se utiliza la clase ticker y el método setticker
para mostrarlo.
private Ticker hi = new Ticker("Teletipo ¡HOLA!");
TextBox t = new TextBox("MID1", 256, 0);
t.setTicker(hi);
display.setCurrent(t);
5.6.2. Iconos
Agregar iconos a las MIDlets es sumamente sencillo, sólo hay que agregar los
nombres, incluyendo rutas de acceso a partir del archivo jad, del archivo de ícono
que queramos agregar a cada MIDlet de nuestro paquete, en el archivo descriptor.
86
mailxmail - Cursos para compartir lo que sabes
MIDlet-1: MID,icono.png,MIDlet1
MIDlet-2: MID1,ícono2.png,MIDlet2
Los iconos a utilizar deberán estar, generalmente, en formato png, por lo que
si se tiene el ícono que se quiere utilizar en otro formato, es recomendable
convertirlo, aunque los equipos modernos ya soportan imágenes en jpg.
5.7.Mensajería
Según JCP (Java Community Procces), en su solicitud de especificación (JSR)
120, WMA (Wireless Messaging API, api de mensajería inalámbrica) es un paquete
opcional de J2ME. Esta api está basada en la Estructura de Conexión Genérica
(Generic Conection Framework, GCF) y orientada a CLDC, aunque también puede ser
implementada en perfiles CDC y algunas aplicaciones de J2SE, esto último, una vez
que la JSR 197 (el paquete opcional GCF para J2SE) esté completa. En la figura 5.4 se
pueden ver los componentes de la api de mensajería inalámbrica.
Figura 5.4. Componentes de WMA.
Todo lo necesario para utilizar la api de mensajería está contenido en el
paquete javax.wireless.messaging, el cual contiene las interfaces requeridas para
87
mailxmail - Cursos para compartir lo que sabes
paquete javax.wireless.messaging, el cual contiene las interfaces requeridas para
mandar y recibir mensajes inalámbricos, tanto binarios como de texto. La tabla 5.2
describe el contenido de este paquete.
Tabla 5.2. Contenido de la api WMA (Wireless Messaging API)
Interfaz
Descripción
Métodos
Message
Interfaz base, de la cual
se derivan subinterfaces
como BinaryMessage y
TextMessage.
getAdress(), getTimestamp(),
setAdress().
BinaryMessage
Contiene métodos para
definir y ver el contenido getPayloadData(), setPayloadData().
binario de los mensajes.
TextMessage
Para obtener y establecer
contenidos de texto en
los mensajes, SMS (Short getPayloadText(), setPayloadText().
Message System) por
ejemplo.
Subinterfaz de la GCF
con la cual se pueden
MessageConnection
crear, enviar y recibir los
mensajes.
MessageListener
newMessage(), send(),
receive(),setMessageListener(),
numberOfSegments().
Define un escuchante
para implementar la
notifyIncomingMessage().
notificación asíncrona de
mensajes entrantes.
5.7.1.Excepciones
Utilizando WMA, el programador se enfrenta a algunas excepciones que deben
ser controladas para asegurar la estabilidad de la aplicación. En la tabla 5.3 se
muestran y describen las excepciones que pueden arrojar los métodos de la api de
mensajería.
Tabla 5.3. Excepciones en WMA.
Excepción
Lanzada por
88
Descripción
mailxmail - Cursos para compartir lo que sabes
ConnectionNotFoundException Connector.open().
La conexión no puede ser
realizada o no existe ese
tipo de conexión.
InterruptedIOException
receive(), send().
La conexión se cerró
mientras se recibía un
mensaje o se ha excedido
el tiempo de espera al
enviar.
IllegalArgumentException
Connector.open(),
métodos
open*Stream(),
newMessage(), send().
Algún argumento de
open() es inválido, no se
soporta conexiones de
tipo Stream, el tipo de
mensaje no se ha
definido o el mensaje
contiene información no
válida.
IOException
Connector.open(),
receive(), send(),
setMessageListener().
Ha ocurrido un error de
entrada/salida o se ha
cerrado la conexión
inesperadamente.
NullPointerException
send().
El parámetro Message es
nulo.
SecurityException()
Connector.open(),
receive(), send(),
setMessageListener().
El protocolo solicitado no
esta disponible, no hay
permiso para recibir o
mandar mensajes por el
puerto especificado,
5.7.2.La interfaz Message
Esta interfaz es la base de todos los tipos de mensajes que se crean envían y
reciben utilizando WMA.
En algunos aspectos, un mensaje es similar a un datagrama: ambos tienen
direcciones origen y destino, contenido y maneras de enviar y bloquear un mensaje.
WMA agrega soporte para mensajes binarios y de texto y una interfaz escuchante
para recibir mensajes de manera asincrona[1].
89
mailxmail - Cursos para compartir lo que sabes
Según la especificación de la api de mensajería, se definen dos subinterfaces
para la interfaz Message,TextMessage y BinaryMessage.
5.7.3.La interfaz BinaryMessage
Esta subinterfaz representa un mensaje con contenido binario (EMS o
Enhanced Message System, mensajes multimedia) y define métodos para introducir
y recibir tal contenido.
5.7.4.La interfaz TextMessage
Se utiliza para obtener e introducir contenido de texto simple (SMS, Short
Message System) de y en los mensajes.
5.7.5.La interfaz MessageConnection
Es una subinterfaz del paquete javax.microedition.connection de la GCF,
contiene los métodos newMessage(), send() y receive(); para crear, enviar y recibir
mensajes, respectivamente. En la figura 5.5 se muestra la interfaz MessageConnection
MessageConnection y su relación con la GCF.
90
mailxmail - Cursos para compartir lo que sabes
Figura 5.5. MessageConnection y su relación con GCF.
Con MessageConnection, como con cualquier otra conexión GCF, se utiliza Connector.ope
Connector.open() para crear (abrir) un conexión y Connector.close() para cerrarla[2].
Una conexión se puede crear de las siguientes dos maneras:
1.Enviar.
Una conexión en la que sólo se pueden enviar mensajes se crea de la
siguiente manera:
(MessageConnection)Connector.open("sms://+6251001287:5000");
91
mailxmail - Cursos para compartir lo que sabes
Donde sms será el protocolo por medio del cual se enviará el mensaje, 6251001287
6251001287 el número telefónico que lo recibirá y 5 0 0 0 el puerto por el cual se
habrá de realizar la operación.
2.Recibir.
Este tipo de conexión se crea indicando sólo el protocolo y el puerto que se
habrán de utilizar para la recepción de los mensajes.
(MessageConnection)Connector.open("sms:// :5000");
Si se intenta utilizar una dirección local (número de puerto) antes reservada se
provoca una excepción de entrada/salida (IOException).
Se ha mencionado que para crear un mensaje se debe definir el protocolo, la
especificación de WMA contempla los siguientes protocolos:
- SMS: Sistema de mensajes cortos(Short Messaging System), mensajes de
texto plano. Estos mensajes son bidireccionales, es decir, se pueden enviar y recibir
desde cualquier dispositivo.
- EMS:Sistema de mensajes mejorados (Enhanced Messaging System), los
recientemente presentados mensajes multimedia utilizan este protocolo
bidireccional.
- CBS:Mensaje corto de difusión en celda (Cell Broadcast Short message), estos
mensajes son unidireccionales, es decir, son enviados por una estación base y los
equipos portátiles únicamente pueden recibirlos. Si se intenta enviar un mensaje
utilizando "cbs" se obtendrá una IOException.
Algunos proveedores pueden implementar protocolos adicionales y nuevas
subinterfaces de Message de ser necesario.
92
mailxmail - Cursos para compartir lo que sabes
5.7.6.La interfaz MessageListener
Con esta interfaz se puede implementar un escuchante para recibir mensajes
de manera asíncrona, es decir, sin bloquear el aparato mientras se espera un
mensaje. MessageListener contiene únicamente un método:
notifyIncommingMessage(), este es invocado por la plataforma cuando se recibe un
mensaje. Para iniciar un escuchante se debe usar el método MessageConnection.setListener()
MessageConnection.setListener().
Debido a que algunos equipos son monohilo (monothreaded), se debe ser
muy cuidadoso de mantener el uso de MessageListener al mínimo. Se puede lanzar
un hilo separado para capturar y procesar el mensaje, así la plataforma gasta el
menor tiempo posible en notificar el nuevo mensaje.
5.7.7.Enviando y recibiendo mensajes
5.7.7.1.Crear una conexión
Este es el primer paso a realizar cuando se desean enviar o recibir mensajes.
A continuación se muestra como instanciar un objeto de la clase MessageConnection
MessageConnection.
MessageConnection mc=(MessageConnection)Connector.open(Dir);
Donde m c es la conexión a crear y Dir es la variable String que contiene la
cadena de dirección a la que se enviará o de la que se espera el mensaje
("sms://+1234567:5000", por ejemplo). Es recomendable utilizar un método que
tome como parámetro la dirección e instancie el objeto basándose en ella, esto se
puede hacer de la siguiente forma:
93
mailxmail - Cursos para compartir lo que sabes
public MessageConnection NuevoMensaje(String Dir)
throws Exception {
return((MessageConnection)Connector.open(Dir));
}
5.7.7.2.Crear y enviar mensajes de texto
Para enviar un mensaje de texto, se muestra a continuación un ejemplo en el
que se emplea un método que toma como parámetros un objeto conexión y dos
cadenas de caracteres: mensaje y dirección.
El primer paso es crear un mensaje de texto.
public void EnviarTexto(MessageConnection mc, String msg, String Dir) {
try {
TextMessage tmsg = (TextMessage)mc.newMessage
(MessageConnection.TEXT_MESSAGE);
Una vez creado el mensaje, se le introducen valores de dirección (Dir) y
contenido (msg).
if (Dir!= null)
tmsg.setAddress(Dir);
tmsg.setPayloadText(msg);
Ahora está listo para ser enviado.
94
mailxmail - Cursos para compartir lo que sabes
mc.send(tmsg);
}
También se implementa el control de excepciones.
catch(Exception e) {
System.out.println(Error al enviar mensaje " + e);
}
}
5.7.7.3.Crear y enviar mensajes binarios
El procedimiento para la creación y envío de mensajes binarios es muy similar
al de los mensajes de texto, únicamente hay que sustituir algunos valores y métodos.
public void EnviarTexto(MessageConnection mc, byte[] msg, String Dir) {
try {
BinaryMessage bmsg = (BinaryMessage)mc.newMessage
(MessageConnection.BINARY_MESSAGE);
if (Dir!= null)
bmsg.setAddress(Dir);
bmsg.setPayloadData(msg);
mc.send(bmsg);
}
catch(Exception e) {
95
mailxmail - Cursos para compartir lo que sabes
System.out.println(Error al enviar mensaje " + e);
}
}
Notese que en los mensajes binarios no se utilizan contenidos de tipo cadena
de texto, sino que se utilizan arreglos de bytes, esto debido a que su tratamiento
será como términos binarios, es decir bytes.
5.7.7.4.Esperar mensajes entrantes
Para esperar la entrada de un mensaje, normalmente se lanza un hilo (thread)
para invocar al método Message.receive(), el cual capturará el mensaje. Una vez que
se ha recibido el mensaje, es necesario determinar su tipo (texto, binario u otro)
para así llamar al método apropiado para ver su contenido.
El siguiente código muestra un hilo de ejecución responsable de recibir y
procesar mensajes.
class Mensajero implements Runnable {
Thread th = new Thread(this);
MessageConnection mc; // MessageConnection a manejar.
boolean hecho; // si es verdadero, el hilo termina.
boolean uno; // si es verdadero se procesa sólo un mensaje y sale
El siguiente constructor se utilizará para lanzar un hilo que procese múltiples
mensajes.
public Mensajero(MessageConnection mc) {
96
mailxmail - Cursos para compartir lo que sabes
public Mensajero(MessageConnection mc) {
this.mc = mc;
th.start();
}
Debido a que resultaría un gasto innecesario de procesamiento si sólo se
quiere un mensaje, se puede utilizar el constructor descrito a continuación.
public Mensajero(MessageConnection mc, boolean uno) {
this.mc = mc;
this.uno = uno;
th.start();
}
Se notifica cuando el hilo haya terminado su trabajo:
public void notifyDone() {
done = true;
}
El método r u n del hilo para esperar y procesar mensajes recibidos. Si se ha
indicado en el constructor que se desea sólo un mensaje se llama el procedimiento procesar()
procesar(), de otra manera se revisan todos los mensajes.
public void run() {
if (singlepass == true)
procesar();
97
mailxmail - Cursos para compartir lo que sabes
procesar();
else
RevisarTodo();
}
El procedimiento RevisarTodo(), el cual procesa todos los mensajes recibidos.
public void RevisarTodo() {
while (!hecho)
procesar();
}
Para procesar un solo mensaje:
public void procesar() {
Message msg = null;
// Se intenta recibir un mensaje
try {
msg = mc.receive();
}
catch (Exception e) {
System.out.println("Error al recibir mensaje " + e);
}
// Se procesa el mensaje recibido
//Se compara si msg es una instancia de TextMessage
if (msg instanceof TextMessage) {
98
mailxmail - Cursos para compartir lo que sabes
if (msg instanceof TextMessage) {
TextMessage tmsg = (TextMessage)msg;
// Se maneja como mensaje de texto
ticker.setString(tmsg.getPayloadText());
}
else {
// Se compara si es mensaje binario
if (msg instanceof BinaryMessage) {
BinaryMessage bmsg = (BinaryMessage)msg;
byte[] data = bmsg.getPayloadData();
// se maneja como tal, mostrándolo en un visor multimedia, tal
//vez
}
else {
// Se ignora cualquier otro tipo
}
}
}
Hay que recordar que el código anterior pertenece a un hilo de ejecución, por
lo que debe ir dentro de una clase principal y debe llamarse de la siguiente manera:
Mensajero Msj = new Mensajero (mc);
// o
Mensajero Msj = new Mensajero (mc,true);
99
mailxmail - Cursos para compartir lo que sabes
Teniendo, obviamente, un objeto de la clase MessageConnection llamado m c.
Con lo expuesto en este apartado se puede realizar una utilización modesta
del servicio de mensajería de J2ME. Se recomienda a los interesados en la utilización
de este servicio en sus aplicaciones la revisión de la literatura indicada en la
bibliografía del presente documento, esto para la profundización en temas de
seguridad, segmentación y otros que pueden colaborar a la creación de aplicaciones
más robustas y potentes.
5.8.Almacenamiento persistente
MIDP proporciona un mecanismo para las MIDlets, el cual les permite el
almacenamiento de datos de manera que éstos permanezcan en el equipo aunque la
MIDlet se cierre y puedan ser recuperados cuando se solicite. Este mecanismo es
una base de datos muy simple llamada Sistema de Administración de Registros
(RMS, por sus siglas en inglés).
Los registros de RMS son archivos binarios cuya codificación es dependiente
del hardware, ya que son creadas en localidades dependientes de la plataforma.
Las apis para manejo de registros y conjuntos de registros (archivos) proveen
las siguientes funcionalidades:
-Eliminación y agregado de registros.
-Se permite a MIDlets de la misma aplicación compartir los registros.
-No existe un mecanismo para compartir registros o archivos entre diferentes
aplicaciones.
-Una aplicación puede, además de crear, borrar y modificar sus archivos.
Como casi todo en Java y en especial en J2ME, los nombres de archivos son
100
mailxmail - Cursos para compartir lo que sabes
Como casi todo en Java y en especial en J2ME, los nombres de archivos son
sensibles a mayúsculas y minúsculas, además no deben tener un nombre con
longitud mayor a 32 (treinta y dos) caracteres. No se puede crear archivos con
nombres duplicados. En el caso de los simuladores, al crear un archivo en una
aplicación MIDP, este se ubicará en un directorio llamadoNOJAM, por ejemplo, si se
tiene el WTK en el directorio "/usr/local/JWTK2.0/" los archivos se crearán en el
directorio "/usr/local/JWTK2.0/NOJAM".
Se puede acceder a un archivo desde varios hilos (threads) al mismo tiempo,
sólo hay que mantener la atención en la sincronización de tales hilos, un error
podría causar la pérdida de los datos.
5.8.1.El paquete RMS
El paquete RMS consta de cuatro interfaces, una clase y cinco clases de
excepción, a continuación se explica cada una de ellas.
1.Interfaces:
-RecordComparator: Define una herramienta para comparar dos registros.
-RecordEnumeration: Representa un enumerador bidireccional de registros.
-RecordFilter: Define un filtro para examinar un registro y comprobar que
coincida con un criterio establecido.
-RecordListener: Escuchante de adhesiones, modificaciones y eliminaciones
de registros. Captura el registro procesado.
2.Clase:
-RecordStore: Representa un conjunto de registros, es decir, un archivo.
3.Excepciones:
101
mailxmail - Cursos para compartir lo que sabes
-InvalidRecordIDException: Es lanzada (throw) para indicar que el ID de un
registro no es válido.
-RecordStoreException: Cuando una excepción general es lanzada.
-RecordStoreFullException: Indica que el sistema de archivos para esa
aplicación está lleno (actualmente se permiten 30KB por aplicación).
-RecordStoreNotFoundException: Se lanza cuando no se ha podido encontrar
el archivo.
-RecordStoreNotOpenException: Ocurre cuando se trata de acceder a un
archivo que no ha sido abierto.
5.8.2.Programando con RMS
A continuación se muestra cómo realizar algunas de las operaciones básicas
en el manejo de archivos utilizando el paquete RMS.
5.8.2.1.Abrir o crear un archivo
Para hacer esto, se utiliza el método openRecordStore() de la siguiente forma:
RecordStore db=new RecordStore.openRecordStore("miArchivo",true);
Donde "miArchivo" es el nombre del archivo que se desea abrir y true indica si
se creará el archivo en caso de no existir.
Si se llama el método openRecordStore() sobre un archivo abierto, se generará
una referencia al objeto RecordStore() abierto.
5.8.2.2.Agregar un registro
102
mailxmail - Cursos para compartir lo que sabes
Un registro se almacena como un arreglo de bytes. Para agregarlos al archivo
y empaquetar los tipos de datos en el arreglo se emplean las clases DataOutputStream y ByteAr
DataOutputStream y ByteArrayOutputStream. El primer registro que se crea tiene una
ID de 1, esa es su llave primaria. El valor de ID irá aumentando en 1 según se
agreguen registros. El siguiente código muestra, sin manejo de excepciones (en una
aplicación real se deben manejar), como agregar un registro a la base de datos.
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeUTF(record);
byte[] b = baos.toByteArray();
db.addRecord(b, 0, b.length);
Se comienza por instanciar los objetos DataOutputStream y
ByteArrayOutputStream, acto seguido se agrega el registro como una cadena de
caracteres (se puede utilizar el formato "campo1,campo2,campo3", siendo ',' un
carácter separador), después se agrega un nuevo registro al archivo, dejando así el
espacio preparado para agregar un nuevo registro.
5.8.2.3.Leer datos de un archivo
Al leer datos de un archivo, se deben utilizar los objetos DataInputStream y
ByteArrayInputStream, esto se hace como sigue:
ByteArrayInputStream bais = new
ByteArrayInputStream(record1);
DataInputStream dis = new
DataInputStream(bais);
103
mailxmail - Cursos para compartir lo que sabes
DataInputStream(bais);
String in = dis.readUTF();
El objeto record1 es un arreglo de bytes que puede ser generado como
resultado de ejecutar el método miRS.enumerateRecords(null,null,false).
5.8.2.4.Comparar registros
Cuando se necesita buscar un registro en la base de datos, ya sea para
mostrarlo al usuario o para eliminarlo, esto hace necesaria la comparación entre
registros o de registros con cadenas de caracteres. En J2ME no se puede hacer una
comparación directa de tiposString, así que se debe usar el método
compareTo(String) de la clase String y quizá sea necesario, como en el ejemplo
siguiente, implementar la interfaz Comparator.
public Clase implements Comparator {
public int comparar(byte[] reg1,byte[] reg2) {
ByteArrayInputStream bais1 = new ByteArrayInputStream(reg1);
DataInputStream dis1 = new DataInputStream(bais1);
ByteArrayInputStream bais2 = new ByteArrayInputStream(reg2);
DataInputStream dis2 = new DataInputStream(bais2);
String nom1 = dis1.readUTF();
String nom2 = dis.readUTF();
int num = nom1.compareTo(nom2);
if (num > 0) {
return RecordComparator.FOLLOWS;
} else if (num < 0) {
104
mailxmail - Cursos para compartir lo que sabes
} else if (num < 0) {
return recordcomparator.PRECEDES;
} else {
return recordcomparator.EQUIVALENT;
}
}
}
Después de abrir dos registros del mismo archivo se realiza la comparación
entre ellos con el método compareTo, el cual regresa 0 si ambos elementos son
iguales, un número mayor a 0 si el primero ( nom 1) es mayor que el segundo (nom 2)
y menor a 0 en el caso inverso.
La función arriba descrita devuelve uno de los siguientes valores constantes:
-FOLLOWS: 1, cuando el primer parámetro es mayor que el segundo.
-PRECEDES: -1,cuando el segundo es mayor que el primero.
-EQUIVALENT: 0, cuando son equivalentes.
5.8.2.5.Borrar registros
Para borrar un registro se debe conocer su ID y J2ME no proporciona ningún
método para obtenerlo directamente, una técnica recomendada es agregar un
contador a la función de comparación, abrir únicamente un registro y compararlo
con la cadena de caracteres que buscamos en el registro y devolver el valor del
contador en lugar del resultado de la comparación.
Una vez que se ha localizado el registro, se utiliza Recordstore.deleteRecord
105
mailxmail - Cursos para compartir lo que sabes
(ID), por ejemplo, si el RecordStore utilizado se llama RS1 y la cadena de caracteres a
buscar en el registro es buscado, la eliminación será así:
re=RS1.enumerateRecords(null,null,false);
while(re.hasNextElement){
Supongamos que se han hecho algunas modificaciones a comparar como la
de sustituir un parámetro byte[] por un String.
id=comparar(re.NextRecord(),buscado);
if(id!=-1)
RS1.deleteRecord(id);
}
5.8.2.6.Cerrar el archivo
Para cerrar un archivo solo basta con llamar el método closeRecordStore(), por
ejemplo:
try{
datos.closeRecorStore();
}catch(Exception e){
}
5.9.Multimedia
Los más modernos equipos celulares y otra clase de dispositivos pequeños de
106
mailxmail - Cursos para compartir lo que sabes
Los más modernos equipos celulares y otra clase de dispositivos pequeños de
alta tecnología tienen gran potencia para la reproducción de contenidos multimedia,
principalmente a través de Internet, puesto que, debido a sus escasas capacidades
de almacenamiento no podrían contener, un clip de video, por ejemplo, en el
espacio reservado para tal fin.
Música en formatos WAV, MDI y MP3, animaciones y videos en formatos MPG,
MOV o AVI; son hasta ahora los contenidos multimedia que la api opcional de
multimedia de J2ME puede reproducir o hasta grabar, dependiendo, obviamente de
las capacidades del aparato al que las MIDlets con tal contenido estarán dedicadas.
La MMAPI (MultiMedia API) ha sido diseñada para correr sobre cualquier
máquina virtual basada en J2ME, esto incluye las máquinas virtuales de CLDC (KVM)
y CDC. Esta api es un paquete opcional de J2ME e incluye las siguientes
características:
-Soporte para generación de tonos, reproducción y grabado de contenidos de
audio y video basados en tiempo (música y video tradicional).
-Bajo consumo de recursos, MMAPI se ajusta a las especificaciones de CLDC.
-La api nunca conoce el contenido o protocolo utilizados.
-El programador puede limitar el soporte a ciertos tipos de contenido, audio
básico, por ejemplo.
-Se ha dejado margen para agregar nuevas características a esta api sin que
haya necesidad de romper con la funcionalidad de las antiguas.
5.9.1.Procesamiento multimedia
El procesamiento de contenido multimedia se divide en dos partes:
1.Manejo de protocolos. Consiste en leer los datos de la fuente, esta puede
107
mailxmail - Cursos para compartir lo que sabes
ser un archivo o un servidor de la red u otro medio de procesamiento.
2.Manejo de contenidos. Descodificar el contenido y presentarlo en un
dispositivo de salida como pueden ser una bocina, una pantalla o ambas.
Para facilitar esta tarea, MMAPI proporciona dos tipos de objetos de alto nivel: Datasource
Datasource y Player.
Datasource encapsula el manejo del protocolo escondiendo los detalles de
lectura de los datos. Los métodos de este objeto habilitan al objeto Player para
interpretar el contenido.
Player, lee los datos arrojados por Datasource, los procesa y los muestra en el
dispositivo de salida. Este objeto provee de métodos para controlar la reproducción
del contenido, esto incluye aquellos métodos para acceder a ciertas características
de tipos medios.
MMAPI especifica un tercer objeto, un controlador conocido como Manager,
para que la aplicación pueda crear reproductores (Players) desde un Datasource y
también de un InputStream.
En la figura 5.6 se observa la arquitectura completa de MMAPI.
108
mailxmail - Cursos para compartir lo que sabes
Figura 5.6. Arquitectura MMAPI.
El objeto Manager provee de un método llamado createPlayer(), utilizado para
crear los objetos Player que habrán de servir de base para la reproducción de
contenidos multimedia.
5.9.1.1.Los paquetes de MMAPI
La api de multimedia de J2ME consta de tres paquetes:
1 .javax.microedition.media: Provee algunas interfaces, una excepción y la
clase Manager, la cual es el intermediario para obtener recursos del sistema (como Player
Player) para el procesamiento multimedia.
2 .javax.microedition.media.control: Define los tipos específicos de control que
se pueden utilizar e un reproductor: VolumeControl, VideoControl y otros.
3 .javax.microedition.media.protocol: Define los protocolos para el manejo de
controles personalizados, incluye la clase Datasource, una abstracción para
manejadores de controles multimedia.
5.9.1.2.Generación de tonos
La generación de tonos, como en algunos lenguajes de programación para
equipos de escritorio, se define por su frecuencia y duración, este tipo de medio es
particularmente importante para los juegos y en equipos pequeños (entre los
pequeños) para los que es su única forma de multimedia.
El método Manager.playTone() es el encargado de generar los tonos, esto se
hace de la siguiente manera:
109
mailxmail - Cursos para compartir lo que sabes
try{
Manager.playTone(ToneControl.C4,4000,100);
}catch (MediaException me){
}
Este fragmento de código reproduce un tono (ToneControl.C4) durante 4
segundos a un volumen de 100 dB.
En los dispositivos más poderosos, se puede utilizar un reproductor para
sintetizar secuencias de tonos.
Player Rep1;
Manager.createPlayer (Manager.TONE_DEVICE_LOCATOR);
Este tipo de reproductor provee de un objeto ToneControl, el cual puede ser
usado para programar una secuencia de tonos.
5.9.1.3.Reproducción de MP3
El siguiente segmento de código muestra como reproducir audio simple sin
detallar totalmente un control de reproducción.
try {
Player p = Manager.createPlayer("http://servidor/musica.mp3");
p.setLoopCount(5);
p.start();
}
110
mailxmail - Cursos para compartir lo que sabes
}
catch(IOException ioe) {
}
catch(MediaException e) {
}
El segmento siguiente incorpora un pequeño control.
Player p;
VolumeControl vc;
try {
p = Manager.createPlayer("http://servidor/musica.mp3");
p.realize();
// obtener control de volumen y establecer volumen al máximo
vc = (VolumeControl) p.getControl("VolumeControl");
if(vc != null) {
vc.setVolume(100);
}
//El reproductor puede iniciar con la menor latencia.
p.prefetch();
p.start();
}
catch(IOException ioe) {
}
catch(MediaException e) {
}
111
mailxmail - Cursos para compartir lo que sabes
}
Debido a que cualquier (InputStream) puede ser utilizado como parámetro en
el método Manager.createPlayer(), una aplicación puede reproducir contenido de un
RMS o de un archivo JAR, A continuación se muestra cómo tomar un InputStream de
un RMS y reproducir lo contenido en él.
RecordStore store;
int id;
// Reproducción desde un registro
try {
InputStream is = new ByteArrayInputStream
(store.getRecord(id));
Player player = Manager.createPlayer(is, "audio/X-wav");
p.start();
}
catch (IOException ioe) {
}
catch (MediaException me) {
}
Como ya se mencionó, también se puede reproducir contenido multimedia
almacenado en un archivo JAR, enseguida se muestra cómo:
try {
InputStream is = getClass().getResourceAsStream("audio.wav");
Player player = Manager.createPlayer(is, "audio/X-wav");
112
mailxmail - Cursos para compartir lo que sabes
p.start();
}
catch(IOException ioe) {
}
catch(MediaException me) {
}
5.9.1.4.Reproducción de video
En el código siguiente se intenta mostrar un video contenido en el archivo
pelicula.mpg.
Player p;
VideoControl vc;
try {
p = Manager.createPlayer("http://servidor/pelicula.mpg");
p.realize();
// Control de video
vc = (VideoControl) p.getControl("VideoControl");
p.start();
}
catch(IOException ioe) {
}
catch(MediaException me) {
}
113
mailxmail - Cursos para compartir lo que sabes
5.9.1.5.Uso de la cámara
MMAPI incluye soporte para la cámara que algunos dispositivos incorporan, la
api tiene un localizador (URL) especial, capture://video, el cual se usa para crear su
reproductor. Se puede utilizar el control VideoControl para mostrar los que la
cámara va captando y VideoControl.getSnapshot(String ImageType), para capturar
una fotografía. El tipo predeterminado (si no se incluye el parámetro) de imagen es
PNG, aunque se puede utilizar cualquiera de los formatos soportados por el equipo,
para saber que formatos son soportados se puede utilizar la propiedad del sistemavideo.snapsh
video.snapshot.encodings
.
5.10. Servlets
Las Java Server Pages (JSP), tambien llamadas servlets (palabra servlet se deriva
de la unión de los términos server y applet) son páginas que, al igual que ASP o PHP,
se ejecutan en un servidor y dan respuesta al cliente en formato de página web
(HTML.
Tomando en cuenta el hecho de que la inmensa mayoría de los dispositivos
compatibles con J2ME lo son también con WML y WAP, resulta obvio considerar que
en estos dispositivos se puedan observar páginas WML. Y si estas páginas pueden
ser generadas de la misma manera como los lenguajes de servidor (ASP, PHP, JSP) lo
hacen con el código HTML, es decir, si al introducir el código que una página en
servidor habrá de generar, se introduce, en lugar de código HTML, código WML. Se
tiene entonces que los dispositivos J2ME también son compatibles con ASP, PHP y JSP.
Debido a que el tema central de este documento es la programación que se
realiza orientada a los dispositivos inalámbricos, el tema de los servlets no se tocará
a fondo, ya que excede los alcances de la investigación
.
5.11. ¿Cómo instalar la aplicación en el dispositivo?
114
mailxmail - Cursos para compartir lo que sabes
5.11. ¿Cómo instalar la aplicación en el dispositivo?
Si la MIDlet está terminada, ahora puede publicarse en un servidor Web, esto
se logra copiando al servidor los archivos .jar y .jad. Una vez en el servidor, la MIDlet
es descargable. Sin embargo se deben agregar algunos tipos MIME (MIMEtypes) a la
configuración del servidor: text/vnd.sun.j2me.app_descriptor.
Para descargar y ejecutar una MIDlet desde un emulador se puede usar la
siguiente instrucción:
midp -transient http://servidor/ruta/archivo.jad.
O bien, desde el JWT, escribir el URL completo de la MIDlet en la caja de texto
del nombre del proyecto del cuadro de diálogo Abrir proyecto.
Es muy importante asegurarse que el servidor esté correctamente configurado
para manejar, tanto los documentos WML, con todos los formatos que estos puedan
incluir, como los archivos .jad y .jar.
[1]La codificación e información de control de los mensajes son específicas
del protocolo y transparentes a WMA.
[2]La clase Connector está contenida en el paquete javax.microedition.io, por
lo que hay que importarlo si se desea emplear la mensajería en la aplicación.
115
mailxmail - Cursos para compartir lo que sabes
6. Desarrollo de una aplicación
6. DESARROLLO DE UNA APLICACIÓN
6.1.Descripción del proyecto
El proyecto en el que se ha pretendido aplicar los conocimientos adquiridos
mediante esta investigación consiste en una pequeña hoja de cálculo con la que se
puedan realizar las operaciones aritméticas básicas (igualación, suma, resta,
multiplicación y división) y combinaciones sencillas de estas (potencia, promedio,
por ejemplo), además de comparaciones entre varios elementos (determinar el valor
mayor o menor de una lista).
También se busca que la aplicación tenga la capacidad de almacenar los
datos de la hoja de cálculo de manera persistente en el equipo, así como de abrir
documentos (hojas) guardados con anterioridad y borrar las hojas, que ya han sido
guardadas previamente, para liberar el limitado espacio de almacenamiento del
dispositivo.
6.2. Herramientas a utilizar
Para la realización de la hoja de cálculo (llamada hojaC, nombre perteneciente
a la clase principal), se ha utilizado una computadora de las siguientes
características:
-Procesador AMD Duron a 1.3 Ghz.
-160 MB de memoria RAM.
-Disco duro de 8.4 Gb.
-Sistema operativo: Mandrake Linux 8.2.
Se inició la escritura del código de hojaC, como se comentó en el capítulo 4,
utilizando un editor de texto llamado Gedit y Java Wireless Toolkit para la
compilación. Aunque esto resultaba sumamente tediosos, por lo que se optó por
utilizar el Entorno Integrado de Desarrollo (IDE) Sun ONE Studio, en una versión de
evaluación especial para la plataforma micro de Java.
6.3. Desarrollo
La aplicación está compuesta por cinco clases principales, de las cuales la
116
mailxmail - Cursos para compartir lo que sabes
llamada hojaC es la que se ejecuta al iniciar y la que inicia el resto de las clases. A
continuación se describen las funciones con que cada clase debe cumplir.
- hojaC: Es la clase MIDlet, es decir, la que se ejecuta. esta clase sólo utiliza
una forma (Form) sobre la que se instancian una clase Tabla y una clase TextField,
de estas, TextField se encarga de mostrar en la parte inferior de la pantalla el valor
de la celda actual, Tabla se explica enseguida.
- Tabla [1]: Esta es la clase que realiza la mayor parte del trabajo, ya que es la
encargada de dibujar la hoja y sus valores, almacenar dichos valores temporalmente,
realizar las llamadas a otras clases y la actualización del campo de texto (TextField).
Esta clase implementa la interfaz CustomItem, la que permite la utilización de
elementos propios de la clase Canvas (eventos de teclas, dibujo de líneas, rellenado
de rectángulos, cambio de colores, etc.) y de los formularios o formas (TextField,
por ejemplo).
- Texto: Hereda o extiende a la clase TextBox, su única función consiste en
capturar los valores de las celdas y enviarlos al arreglo donde los guarda Tabla.
- funcion: Esta clase contiene los métodos necesarios para la resolución de las
fórmulas que se introducen a la hoja de cálculo. Un objeto instanciado en esta clase
se construye cada vez que la hoja se actualiza, esto es, cada que se presiona una
tecla
- Archivo: La clase Archivo es la encargada de todas las operaciones que se
realizan con archivos. En ella se encuentran los métodos destinados a pedir el
nombre o mostrar la lista de los archivos, así como guardarlos, borrarlos o abrirlos,
según lo solicite el usuario.
Ahora se explicará cada clase mostrando aquellos fragmentos de código que
se consideren importantes para tal explicación.
6.3.1.Clase hojaC
La clase hojaC, como el resto de las clases de esta aplicación, forma parte del
paquete hoja. Debido a que será la clase que habrá de albergar a las demás, debe
heredar el paquete midlet, lcdui será para poder utilizar la clase Form. Además la
clase, para poder se ejecutada, necesita extender la clase MIDlet.
package hoja;
117
mailxmail - Cursos para compartir lo que sabes
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class hojaC extends MIDlet implements CommandListener {
Esta clase incluirá los comandos Salir, Guardar, Abrir, Borrar y Nuevo.
También se declaran las clases disp, forma1, tabla, t x t 1 y una variable lógica p r i m
que servirá para identificar cuando sea la primer vez que se ejecuta la clase, esto
sirve para no redefinir los comandos y los valores de algunas variables.
private final static Command Salir = new Command("Salir",
Command.EXIT,
1);
private final static Command Guardar = new Command("Guardar",
Command.SCREEN, 3);
private final static Command Abrir = new Command("Abrir",
Command.SCREEN, 3);
private final static Command Borrar = new Command("Borrar",
Command.SCREEN,
3);
private final static Command Nuevo = new Command("Nuevo",
Command.SCREEN,
3);
private Display disp;
private Form forma1;
private Tabla tabla;
public TextField txt1;
private boolean prim;
En el constructor de la clase sólo se instancian las clases forma1, tabla, t x t 1 y
se le asigna a p r i m el valor inicial de verdadero.
public hojaC(){
118
mailxmail - Cursos para compartir lo que sabes
forma1=new Form("Mini-hoja de cálculo");
tabla=new Tabla(null, Display.getDisplay(this),this);
txt1=new TextField("=", null, 64, 0);
prim=true;
}
En el método startApp() se agregan los comandos, la tabla y el campo de
texto a forma1 y se define esta como objeto a mostrar.
public void startApp() {
if (prim){
disp=Display.getDisplay(this);
forma1.append(tabla);
forma1.append(txt1);
forma1.addCommand(Salir);
forma1.addCommand(Guardar);
forma1.addCommand(Abrir);
forma1.addCommand(Borrar);
forma1.addCommand(Nuevo);
forma1.setCommandListener(this);
prim=false;
disp.setCurrent(forma1);
}else{
disp.setCurrent(forma1);
}
}
public void pauseApp() {
}
119
mailxmail - Cursos para compartir lo que sabes
public void destroyApp(boolean unconditional) {
}
La función settexto es utilizada por la clase Tabla para actualizar el contenido
de t x t 1 cada vez que en la tabla se cambia de celda.
protected void settexto(String texto){
txt1.setString(texto);
}
Finalmente, en el método commandAction se define que se hará en caso de
cada opción del menú.
public void commandAction(Command c, Displayable d){
if (c==Salir){
destroyApp(false);
notifyDestroyed();
}
if(c==Abrir){
Archivo ar=new Archivo('A',tabla.getDatos(),disp,forma1);
forma1.setCommandListener(this);
}
if(c==Guardar){
Archivo ar=new Archivo('G',tabla.getDatos(),disp,forma1);
forma1.setCommandListener(this);
}
if(c==Borrar){
Archivo ar=new Archivo('B',tabla.getDatos(),disp,forma1);
forma1.setCommandListener(this);
}
120
mailxmail - Cursos para compartir lo que sabes
if(c==Nuevo){ tabla.limpiar(); }
}
}
6.3.2.Clase Tabla
Esta clase importará el paquete javax.microedition.lcdui y la clase funcion de
el paquete hoja. Además agrega un comando al menú que había creado hojaC, el
comando cmdAny (que mostrará el texto "Valor"). Se hereda la capacidad de manejo
de herramientas de Canvas en la misma pantalla que elementos de Form.
package hoja;
import javax.microedition.lcdui.*;
import hoja.funcion;
public class Tabla extends CustomItem implements ItemCommandListener {
private final static Command cmdAny = new Command("Valor",
Command.SCREEN,1);
private Display display;
//Ancho de la columna que muestra el número de fila o renglón
private static int num=18;
//Filas y columnas en pantalla
private int rows = 6;
private int cols = 3;
//Filas y columnas reales de la tabla (filas y columnas del arreglo)
private int rrows = 5;
private int rcols = 3;
//Tamaño de una celda
private int dx = 51;
121
mailxmail - Cursos para compartir lo que sabes
private int dy = 19;
//Celda seleccionada
private int currentX = 0;
private int currentY = -1;
//Arreglo donde se almacenarán los datos
private String[][] data = new String[rrows][rcols];
//Indicador de la pantalla vertical y horizontal donde se encuentra[2].
private int pantallaV=1;
private int pantallaH=1;
//Se necesita utilizar una clase hojaC para la actualización de txt1.
private hojaC hoja1;
En el constructor de Tabla se crea una clase CustomItem (super) con title
como parámetro, se asigna valor a d y se definen el comando.
public Tabla(String title, Display d,hojaC hoja) {
super(title);
display = d;
addCommand(cmdAny);
setDefaultCommand(cmdAny);
setItemCommandListener(this);
hoja1=hoja;
}
El método paint es el encargado de dibujar el estado actual de la tabla,
originalmente integra a la clase Canvas, y CustomItem lo hereda de esta. Lo primero
que se hace es dibujar las líneas y los títulos de columna y renglón.
protected void paint(Graphics g, int w, int h) {
for (int i = 0; i <= rows; i++) {
122
mailxmail - Cursos para compartir lo que sabes
for (int i = 0; i <= rows; i++) {
if (i>0&&i<rows){
int ClipX = g.getClipX();
int ClipY = g.getClipY();
int ClipWidth = g.getClipWidth();
int ClipHeight = g.getClipHeight();
g.setClip(1, (i*dy), num - 1, dy - 1);
g.drawString(String.valueOf(i), num-2, ((i + 1) * dy)+1,
Graphics.BOTTOM | Graphics.RIGHT);
g.setClip(ClipX, ClipY, ClipWidth, ClipHeight);
}
if (i==0)
g.drawLine(0,0, cols * dx+num, i * dy);
g.drawLine(0, i * dy, cols * dx+num, i * dy);
}
for (int i = 0; i <= cols; i++) {
if (i>0){
int x;
x=((i * dx)+((i-1)*dx))/2;
int ClipX = g.getClipX();
int ClipY = g.getClipY();
int ClipWidth = g.getClipWidth();
int ClipHeight = g.getClipHeight();
g.setClip(((i-1)*dx)+num, 1, dx - 1, dy - 1);
g.drawString(letrade(i), x+num,dy,
123
mailxmail - Cursos para compartir lo que sabes
Graphics.BOTTOM | Graphics.HCENTER);
g.setClip(ClipX, ClipY, ClipWidth, ClipHeight);
}else
g.drawLine(0,0, 0, rows * dy);
g.drawLine((i * dx)+num, 0, (i * dx)+num, rows * dy);
}
Después se dibuja un rectángulo de color diferente al resto para indicar la
celda seleccionada.
int oldColor = g.getColor();
g.setColor(0x00D0D0D0);
g.fillRect((currentX * dx) + 1+num, ((currentY+1) * dy)+1, dx - 1, dy 1);
g.setColor(oldColor);
Finalmente se busca en el arreglo data todas aquellas celdas que contengan
algún valor y se dibuja su contenido en la posición adecuada.
for (int i = 1; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (data[i-1][j] != null) {
int oldClipX = g.getClipX();
int oldClipY = g.getClipY();
int oldClipWidth = g.getClipWidth();
int oldClipHeight = g.getClipHeight();
funcion func=new funcion(data);
g.setClip((j * dx) + num, i * dy+1, dx - 1, dy - 1);
g.drawString(func.Valor(data[i-1][j]), (j * dx) + num+1,
124
mailxmail - Cursos para compartir lo que sabes
((i + 1) * dy) - 2, Graphics.BOTTOM | Graphics.LEFT);
g.setClip(oldClipX, oldClipY, oldClipWidth, oldClipHeight);
}
}
}
}
El método traverse está definido como integrante de CustomItem, esta clase
utiliza los eventos de teclas de la clase Canvas para lograr el desplazamiento por las
celdas de la tabla.
protected boolean traverse(int dir, int viewportWidth, int viewportHeight,
int[]
visRect_inout) {
switch (dir) {
case Canvas.DOWN:
if (currentY < (rrows - 1)) {
currentY++;
repaint(currentX * dx, currentY * dy, dx, dy);
repaint(currentX * dx, currentY+1 * dy, dx, dy);
} else {
pantallaV++;
}
break;
case Canvas.UP:
if (currentY > 0) {
currentY--;
repaint(currentX * dx, (currentY + 2) * dy, dx, dy);
125
mailxmail - Cursos para compartir lo que sabes
repaint(currentX * dx, (currentY + 2) * dy, dx, dy);
repaint(currentX * dx, (currentY+1) * dy, dx, dy);
} else {
pantallaV--;
return false;
}
break;
case Canvas.LEFT:
if (currentX > 0) {
currentX--;
repaint((currentX + 1) * dx, currentY * dy, dx, dy);
repaint(currentX * dx, currentY * dy, dx, dy);
}else{
pantallaH--;
}
break;
case Canvas.RIGHT:
if (currentX < (rcols - 1)) {
currentX++;
repaint((currentX - 1) * dx, currentY * dy, dx, dy);
repaint(currentX * dx, currentY * dy, dx, dy);
}else{
pantallaH++;
}
}
126
mailxmail - Cursos para compartir lo que sabes
visRect_inout[0] = currentX;
visRect_inout[1] = currentY;
visRect_inout[2] = dx;
visRect_inout[3] = dy;
//Se actualiza el contenido de txt1, de la clase hoja.
hoja1.settexto(data[currentY][currentX]);
return true;
}
Se utiliza setText para almacenar en el arreglo data una cadena de caracteres
que corresponde al valor de la celda actual.
public void setText(String text) {
if (text.compareTo("")==0){
text=null;
}
data[currentY][currentX] = text;
currentY--;
repaint(currentY * dx, currentX * dy, dx, dy);
}
Cuando en el menú se selecciona la opción "Nuevo", la clase hojaC llama al
método limpiar, el cual recorre toda la tabla eliminando cualquier valor que ésta
tenga y actualizando la celda.
public void limpiar() {
for(int y=0;y<rrows;y++)
for(int x=0;x<rcols;x++){
data[y][x] = null;
127
mailxmail - Cursos para compartir lo que sabes
repaint(y * dx, x * dy, dx, dy);
}
}
Las funciones getdato, getText y getDatos se emplean para obtener el valor
de la celda actual, de una celda determinada y de toda la tabla, respectivamente.
protected String getdato(){
return data[currentY][currentX];
}
protected String getText(int x, int y){
return data[y][x];
}
protected String[][] getDatos(){
return data;
}
Al seleccionar la opción "Valor" del menú se asigna a modo el valor para
admitir cualquier carácter y se crea una instancia de Texto, mostrándola en pantalla.
public void commandAction(Command c, Item i) {
if (c==cmdAny){
modo=TextField.ANY;
Texto txt1 = new Texto(data[currentY][currentX], this, display, modo);
display.setCurrent(txt1);
}
}
Por efecto de claridad y espacio, se han omitido algunos fragmentos del
código de la clase Tabla que se han considerado innecesarios en la explicación del
funcionamiento de la clase, aunque eso no significa que no sean vitales para la
128
mailxmail - Cursos para compartir lo que sabes
operación real de la misma.
6.3.3.Clase Texto
La clase Texto extiende a la clase TextBox, motivo por el cual debe importar
javax.microedition.midlet.MIDlet, aunque no sea una clase ejecutable por sí sola. Se
utiliza una clase Tabla, una clase Display para mostrar el TextBox, los comandos CMD_OK
CMD_OK y CMD_CANCEL para insertar el texto en la tabla o cancelar la modificación
y una variable String que habrá de almacenar el texto ingresado.
package hoja;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.MIDlet;
public class Texto extends TextBox implements CommandListener {
private final static Command CMD_OK = new Command("OK",
Command.OK,1);
private final static Command CMD_CANCEL = new Command("Cancelar",
Command.CANCEL,1);
private Tabla parent;
private Display display;
private String text;
El constructor toma como parámetros una cadena de texto, una clase Tabla
que es la que lo llama, una clase Display donde se ha de mostrar ca caja de texto y
una variable entera modo, esta variable entera está pensada para la posible
utilización de varios formatos de cadenas de texto (numérico, carácter, correo
electrónico, etc.). Los valores de los parámetros tomados se asignan a los objetos
con el mismo nombre declarados en esta clase.
public Texto(String text, Tabla parent, Display display, int modo) {
super("Ingrese valor", text, 64, modo);
this.parent = parent;
129
mailxmail - Cursos para compartir lo que sabes
this.display = display;
this.text=text;
addCommand(CMD_OK);
addCommand(CMD_CANCEL);
setCommandListener(this);
}
El método commandAction utiliza el método setText de la clase Tabla (parent
en este caso) y regresa display a dicha clase si se selecciona el comando CMD_OK,
de los contrario no utiliza setText.
public void commandAction(Command c, Displayable d) {
if (c == CMD_OK) {
parent.setText(getString());
display.setCurrentItem(parent);
} else
if (c == CMD_CANCEL) {
display.setCurrentItem(parent);
}
}
}
6.3.4.Clase funcion
Al igual que las hojas de cálculo para computadoras de escritorio, ésta
permite la utilización de fórmulas para realizar operaciones, ya sea con valores
constantes o con valores introducidos dentro de las celadas de la hoja. El formato
para la utilización de fórmulas en hojaC es el siguiente:
operación(operando1, operando2, ... , operando n)
Donde los operandos pueden ser números enteros, celdas de la hoja (A1, B3,
C4, por ejemplo) u otras funciones y operación se sustituye con el símbolo
130
mailxmail - Cursos para compartir lo que sabes
C4, por ejemplo) u otras funciones y operación se sustituye con el símbolo
correspondiente a la operación que se desee realizar, tales símbolos se muestran en
la tabla 6.1.
Tabla 6.1. Símbolos utilizados en las funciones de la hoja de cálculo.
Símbolo Función
Sintaxis
+
Suma.
+(op1, op2, ... , opn) +(A1,2,8,B4)
-
Resta (A-B).
-(op1, op2)
*
Multiplicación.
*(op1, op2, ... , opn) *(4,5,2,1,3)
/
División (A/B).
/(op1, op2)
<
Mínimo.
<op1, op2, ... , opn) <(32,1,0,A2)
>
Máximo.
>op1, op2, ... , opn) >(C3,A2,B1,5)
%
Tanto por ciento (A * B%). %(op1, op2)
:
Promedio.
:(op1, op2, ... , opn) :(10,9,8,7,6)
@
Potencia.
@(base, potencia)
@(9,2)
Igualación.
=(Valor)
=(B2)
=
Ejemplo
-(356,*(A1,3))
/(25,5)
%(100,25)
A continuación se explica el código fuente de esta clase:
Se utilizará una variable data, la cual es una arreglo de String (igual al data de
la clase Tabla), para tener acceso a todos los valores de la hoja, debido a que una
función puede hacer referencia al valor de cualquier celda.
private static String data[][];
int lev=0;
public funcion(String[][] data1){
data=data1;
}
La función getDato toma como parámetro el nombre de una celda (A1, B2,
etc.), lo transforma a coordenadas en la tabla ([1][2], por ejemplo) para después
obtener el valor contenido en esa localidad, ese valor es el que esta función
devuelve como cadena de caracteres.
private String getDato(String celda){
int x,y;
131
mailxmail - Cursos para compartir lo que sabes
int x,y;
String a;
char b;
try{
a=celda.substring(1);
y=Integer.parseInt(a);
b=celda.charAt(0);
x=Character.digit(b,Character.MAX_RADIX)-9;
}catch (Exception e){
return "Error";
}
if ((x<1)||(y<1)||(x>24)||(y>65))
return "Error";
a=data[y-1][x-1];
if (a==null||a.compareTo("")==0)
return "0";
return data[y-1][x-1];
}
Los valores de las celdas que empleará una operación (suma, resta. división
etc.) se almacenan en un objeto de la clase Vector, lo más similar en Java a los
arreglos dinámicos de C++, de manera que primero se deben introducir todos los
datos al vector y después se procesan según lo solicite la fórmula.
En la suma se convierten a enteros todos los términos del vector y se suman
los resultados de tal conversión. Si alguno de los términos no se puede convertir (no
es la representación de un entero) la función devuelve la constante -32767 que se
ha designado como error.
private int suma(Vector datos){
132
mailxmail - Cursos para compartir lo que sabes
private int suma(Vector datos){
int result=0,R=0;
for(int x=0;x<datos.size();x++){
try{
R=Integer.parseInt(datos.elementAt(x).toString());
}catch (Exception e){
return -32767;
}
result+=R;
}
return result;
}
La resta se puede realizar únicamente entre dos elementos enteros, si el
vector tiene más elementos o si alguno de ellos no se ha podido convertir a entero,
se devuelve la constante de error (-32767), de lo contrario la resta se realiza y se
devolverá el resultado.
private int resta(Vector datos){
int result,r=0,R=0;
int s=datos.size();
if(s==2){
try{
R=Integer.parseInt(datos.elementAt(0).toString());
r=Integer.parseInt(datos.elementAt(1).toString());
}catch (Exception e){
return -32767;
}
133
mailxmail - Cursos para compartir lo que sabes
}
result=R-r;
}else{
result=-32767;
}
return result;
}
El proceso de la igualación es muy sencillo, sólo se verifica que en el vector
exista un único elemento y se regresa ese valor, o -32767 de no ser así.
private String igual(Vector datos){
if(datos.size()==1){
return datos.elementAt(0).toString();
}else{
return "Error";
}
}
El proceso de multiplicación es igual al de la suma, se convierten los
elementos a entero y se procesan (multiplican, en este caso) acumulativamente.
private int multi(Vector datos){
int result=1,R=0;
for(int x=0;x<datos.size();x++){
try{
R=Integer.parseInt(datos.elementAt(x).toString());
}catch (Exception e){
return -32767;
134
mailxmail - Cursos para compartir lo que sabes
}
result*=R;
}
return result;
}
Así como la suma y la multiplicación siguen el mismo procedimiento, la resta
y la división también lo hacen, con las obvias diferencias en la operación medular.
private int divi(Vector datos){
int result,r=0,R=0;
if(datos.size()==2){
try{
R=Integer.parseInt(datos.elementAt(0).toString());
r=Integer.parseInt(datos.elementAt(1).toString());
}catch (Exception e){
return -32767;
}
result=R/r;
}else{
result=-32767;
}
return result;
}
Para calcular máximos y mínimos se utiliza la misma función, incluyendo el
parámetro de entrada m m, el cual habrá de tomar un valor de 'm' si se trata de un
mínimo o 'M' si lo que hay que calcular es el máximo. Al igual que el resto de las
operaciones, el máximo y el mínimo también devuelven la constante de error si los
135
mailxmail - Cursos para compartir lo que sabes
datos del vector no se pueden convertir a enteros.
private int maxmin(Vector datos, char mm){
int M=-32767,x,B;
try{
M=Integer.parseInt(datos.elementAt(0).toString());
}catch(Exception e){
return -32767;
}
for (x=1;x<datos.size();x++){
try{
B=Integer.parseInt(datos.elementAt(x).toString());
}catch(Exception e){
return -32767;
}
if(mm=='M')
if(M<B){
M=B;
}
if(mm=='m')
if(M>B){
M=B;
}
}
return M;
}
136
mailxmail - Cursos para compartir lo que sabes
El cálculo del tanto por ciento también utiliza sólo dos valores, el primero es
el valor base y el segundo el tanto por ciento a calcular.
private int pciento(Vector datos){
int result,r=0,R=0;
if(datos.size()==2){
try{
R=Integer.parseInt(datos.elementAt(0).toString());
r=Integer.parseInt(datos.elementAt(1).toString());
}catch (Exception e){
return -32767;
}
result=R/100 * r;
}else{
result=-32767;
}
return result;
}
Debido a lo similares que son las funciones en su procedimiento, las
siguientes sólo se especificarán en nombre, ya que sería una redundancia
innecesaria el explicar los procesos ya explicados.
Promedio.
private int prom(Vector datos){
int result=0,R=0;
for(int x=0;x<datos.size();x++){
try{
137
mailxmail - Cursos para compartir lo que sabes
R=Integer.parseInt(datos.elementAt(x).toString());
}catch (Exception e){
return -32767;
}
result+=R;
}
result=result/datos.size();
return result;
}
Potencia.
private int pot(Vector datos){
int result,r=0,R=0;
if(datos.size()==2){
try{
R=Integer.parseInt(datos.elementAt(0).toString());
r=Integer.parseInt(datos.elementAt(1).toString());
}catch (Exception e){
return -32767;
}
result=1;
for (int x=0;x<r;x++){
result*=R;
}
}else{
result=-32767;
138
mailxmail - Cursos para compartir lo que sabes
}
return result;
}
La función recurrente Valor es la que realiza la mayor parte del trabajo, toma
el valor de la ceda tal y como se ha introducido, lo analiza para verificar si se trata
de una constante o de una fórmula. En este último caso, separa sus términos,
ordena la obtención del valor final (constante) de los datos y finalmente selecciona
la operación a realizar.
public String Valor(String dato){
char c;
Vector A=new Vector();
String cad;
lev++;
Para evitar referencias circulares, no puede haber más de nueve niveles de
anidación en las fórmulas.
if (lev>9){
dato=null;
return "Error";
}
cad=dato;
//separar términos
c=cad.charAt(0);//se toma el primer carácter (donde está el símbolo)
i f(c=='+'||c=='-'||c==' *' ||c==' /' ||c==' < ' ||c==' > ' ||c==' %' ||c==' :' ||
c=='='||c=='@'||c=='&'){
int pa=0,pc=0,x=0,pp=0,up=0,y=0;
//Verificar paréntesis
139
mailxmail - Cursos para compartir lo que sabes
while (x<cad.length()){
if (cad.charAt(x)=='(')
pa++;
if ((pa==1)&&(x==1))
pp=x;
if (cad.charAt(x)==')')
pc++;
if (pc==pa)
up=x;
x++;
};
//Si no coinciden paréntesis de apertura con de cierre.
if ((pa!=pc)||(pp!=1))
return "Error";
for (x=pp+1; x<up;x++){
if (x<cad.length()){
//si se hace referencia a una celda
String celda=null;
if (((cad.charAt(x)<='Z')&&(cad.charAt(x)>='A'))&&
(cad.charAt(x+1)>='1')&&(cad.charAt(x+1)<='6')){
celda=cad.substring(x,x+2);
//se utiliza la recurrencia para las funciones anidadas.
A.addElement(Valor(getDato(celda)));
lev--;
140
mailxmail - Cursos para compartir lo que sabes
x++;
}else{
y=x;
int par=0;
//Se busca ',' para separar los términos
while ((x<up)&&((cad.charAt(x)!=',')||(par!=0))){
celda=cad.substring(y,x+1);
if (cad.charAt(x)=='(')
par++;
if (cad.charAt(x)==')')
par--;
x++;
};
//se agrega el término al vector
if (celda!=null){
A.addElement(Valor(celda));
//Se disminuye el nivel en una cuando se agrega una
//constante
lev--;
}
}
}
}
}
Una vez habiendo llenado el vector con todos los datos, se selecciona la
141
mailxmail - Cursos para compartir lo que sabes
operación a realizar.
switch (c){
//suma (s1,s2,s3...,sn)
case '+':cad=cad.valueOf(suma(A));break;
//resta(r1,r2)
case '-':cad=cad.valueOf(resta(A));break;
//multiplicación (m1,m2,m3...,mn)
case '*':cad=cad.valueOf(multi(A));break;
//división (d1,d2)
case '/':cad=cad.valueOf(divi(A));break;
//Igual (I)
case '=':cad=igual(A);break;
//porcentaje (P,p)
case '%':cad=cad.valueOf(pciento(A));break;
//máximo (M1,M1,M3..,Mn)
case '<':cad=cad.valueOf(maxmin(A,'m'));break;
//mínimo(m1,m2,m3...,mn)
case '>':cad=cad.valueOf(maxmin(A,'M'));break;
//promedio(p1,p2,p3,...pn)
case ':':cad=cad.valueOf(prom(A));break;
//potencia
case '@':cad=cad.valueOf(pot(A));break;
//raiz
case '&':break;
}
142
mailxmail - Cursos para compartir lo que sabes
if (cad.compareTo("-32767")==0){
cad="Error";
}
return cad;
}
}
6.3.5.Clase Archivo
Con las clases que se han analizado hasta ahora, se tiene ya una aplicación
completamente funcional. Pero falta una parte importante de una hoja de cálculo: el
almacenamiento persistente. De poco serviría poder realizar las operaciones más
complicadas en un teléfono celular si no se puede tener memoria de ellas una vez
que se cierra el programa.
La clase Archivo del proyecto es la que se encarga de la administración del
almacenamiento persistente.
Debido a que esta clase utiliza formularios, comandos y objetos Display, se
deben importar los paquetes javax.microedition.midlet y javax.microedition.lcdui. Y
como se trata de la clase que manejará los archivos, también se debe importar javax.microedit
javax.microedition.rms.
Se definen tres comandos, dos para aceptar y una para salir. Los comandos
para aceptar, son dos porque la interfaz de usuario para abrir un archivo es la
misma que la utilizada para seleccionar el archivo a borrar, está interfaz utiliza uno
y guardar, que tiene una interfaz diferente, utiliza el otro.
package hoja;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.rms.*;
import java.io.*;
public class Archivo implements CommandListener {
143
mailxmail - Cursos para compartir lo que sabes
private final static Command pres= new Command("Aceptar",
Command.OK,1);
private final static Command Salir= new Command("Atrás",
Command.EXIT,1);
private final static Command Aceptar = new Command("Aceptar",
Command.OK, 1);
Se utilizarán las siguientes variables con ámbito "global" en esta clase:
//Arreglo de donde se toman o donde se almacenan los datos.
private String [][] tabla;
//Nombre del archivo.
private String nombre;
//Objeto Display donde se muestran los formularios y la lista.
private Display disp;
//Formulario que se utilizará para guardar un archivo.
private Form forma1;
//Base de datos o archivo.
private RecordStore datos;
//Formulario que se mostrará al terminar la operación (el principal, de hojaC)
private Form hoja1;
//Lista que mostrará los archivos existentes para abrir o borrar.
private List Lista;
//Variable que indica si se guarda, abre o borra el archivo.
char G;
144
mailxmail - Cursos para compartir lo que sabes
//Campo de texto para pedir el nombre del archivo al guardar.
TextField txtnom;
Al construir la clase se igualan las variables locales a los parámetros
solicitados y se prepara la forma o la lista según lo indique G.
public Archivo(char G, String[][] tabla,Display disp, Form hoja1){
this.tabla=tabla;
this.disp=disp;
this.hoja1=hoja1;
this.G=G;
if (G=='G'){
txtnom=new TextField("Nombre del archivo",null, 10,0);
forma1=new Form("GUARDAR un archivo");
forma1.append(txtnom);
forma1.addCommand(pres);
forma1.addCommand(Salir);
forma1.setCommandListener(this);
disp.setCurrent(forma1);
}
else{
ListaArch();
}
}
Si G indica que se ha de abrir o borrar un archivo, se crea la lista de archivos
y se muestran en pantalla.
private void ListaArch(){
145
mailxmail - Cursos para compartir lo que sabes
if (G=='A')
Lista = new List("ABRIR un archivo", Choice.IMPLICIT,
RecordStore.listRecordStores(),null);
else
Lista = new List("BORRAR un archivo", Choice.IMPLICIT,
RecordStore.listRecordStores(),null);
Lista.addCommand(Salir);
Lista.addCommand(Aceptar);
Lista.setCommandListener(this);
disp.setCurrent(Lista);
}
El método commandAction decide que hacer, según el comando que se haya
elegido y el valor de G.
public void commandAction(Command c, Displayable d) {
if (c == pres) {
nombre=txtnom.getString();
Guardar();
}
if (c == Aceptar) {
//
disp.setCurrent(forma1);
nombre=Lista.getString(Lista.getSelectedIndex());
if (G=='A')
Abrir();
if (G=='B')
Borrar();
146
mailxmail - Cursos para compartir lo que sabes
}
if (c == Salir) {
disp.setCurrent(hoja1);
}
}
Para abrir un archivo se sigue el proceso explicado en la sección dedicada a
ese tema en este documento y se llama al procedimiento llenar, el cual se encargará
de mostrar los datos en la pantalla.
private void Abrir(){
RecordEnumeration re;
try{
datos=RecordStore.openRecordStore(nombre,false);
re=datos.enumerateRecords(null,null,false);
String reg;
while (re.hasNextElement()){
try{
llenar(re.nextRecord());
}catch(Exception e){}
}
try{
datos.closeRecordStore();
}catch (Exception e){
System.out.println("Error3");
}
disp.setCurrent(hoja1);
147
mailxmail - Cursos para compartir lo que sabes
}catch (Exception e){
Alert a= new Alert("Error","No se pudo abrir el archivo",null,
AlertType.ERROR);
a.setTimeout(Alert.FOREVER);
disp.setCurrent(a);
}
}
Tomando como parámetro una arreglo de bytes, en este caso un registro de
la base de datos, llenar traduce esos datos para calcular la posición en que ese dato
debe ir y el contenido con que habían sido almacenados.
private void llenar(byte[] b){
int x,y;
String dato,reg;
ByteArrayInputStream bais= new ByteArrayInputStream(b);
DataInputStream dis=new DataInputStream(bais);
try{
reg=dis.readUTF();
x=Integer.parseInt(reg.substring(0,reg.indexOf(",")));
y=Integer.parseInt(reg.substring(reg.indexOf(",")+1,reg.indexOf("|")));
dato=reg.substring(reg.indexOf("|")+1);
tabla[x][y]=dato;
}catch(Exception e){}
}
Para guardar una hoja, primero se busca alguna que tenga el mismo nombre
que el que se ha introducido, de existir, el archivo es eliminado y a continuación se
guarda el archivo nuevo.
148
mailxmail - Cursos para compartir lo que sabes
Debido a que RMS está diseñado para bases de datos, se introducen las
celdas ocupadas (aquellas que tengan un valor) con el siguiente formato:
"X,Y|Valor"
Donde X es el renglón de la tabla donde se encuentra, Y la columna
convertida de letra a número (A=0, B=1, C=2) y Valor es la cadena de caracteres
que el usuario ha introducido.
private void Guardar(){
try{
datos=RecordStore.openRecordStore(nombre,false);
datos.closeRecordStore();
RecordStore.deleteRecordStore(nombre);
}catch (Exception e){
System.out.println("Error");
}
try{
datos=RecordStore.openRecordStore(nombre,true);
/**Formato del registro en el archivo="x,y|valor"*/
String reg="";
//Se recorre toda la tabla.
for (int x=0;x<5;x++){
for (int y=0;y<3;y++){
reg=tabla[x][y];
//Se agrega el registro si la celda tiene valor
if (reg!=null){
reg=String.valueOf(x)+","+String.valueOf(y)+"|"+reg;
149
mailxmail - Cursos para compartir lo que sabes
ByteArrayOutputStream baos=new ByteArrayOutputStream();
DataOutputStream dos= new DataOutputStream(baos);
try{
dos.writeUTF(reg);
byte[] b=baos.toByteArray();
datos.addRecord(b, 0, b.length);
}catch (Exception e){
//Si no se puede agregar el registro
Alert a= new Alert("Error",
"No se pudo abrir el archivo",null,AlertType.ERROR);
a.setTimeout(Alert.FOREVER);
disp.setCurrent(a);
return;
}
}
}
}
}catch (Exception e){
//Si no se puede crear el archivo
Alert a= new Alert("Error","No se pudo abrir el archivo",null,
AlertType.ERROR);
a.setTimeout(Alert.FOREVER);
disp.setCurrent(a);
return;
}
150
mailxmail - Cursos para compartir lo que sabes
try{
//Cerrar el archivo
datos.closeRecordStore();
}catch (Exception e){}
//Regresa a la vista principal.
disp.setCurrent(hoja1);
}
El procedimiento para borrar un archivo es muy sencillo, sólo se utiliza el
método deleteRecordStore y se muestra una mensaje indicando el resultado de la
operación.
private void Borrar(){
try{
//Borra el archivo
RecordStore.deleteRecordStore(nombre);
//Crea una alerta para el mensaje
Alert a= new Alert("Hecho","Se ha eliminado el archivo",null,
AlertType.INFO);
a.setTimeout(2000);
//Muestra la alerta
disp.setCurrent(a);
Lista.delete(Lista.getSelectedIndex());
}catch (Exception e){
Alert a= new Alert("Error","No se pudo abrir el archivo",null,
AlertType.ERROR);
a.setTimeout(Alert.FOREVER);
151
mailxmail - Cursos para compartir lo que sabes
disp.setCurrent(a);
}
}
}
6.4. Probar la aplicación
Las pruebas de la aplicación se han realizado en el emulador de un teléfono a
color incluido en Sun ONE Studio 5, en su edición especial para J2ME.
Las figuras 6.1 a 6.8 se muestran las diferentes pantallas (no se incluyen las
de error) que, según las muestra el emulador, que se pueden ver en la mini-hoja de
cálculo.
Al iniciar la aplicación se muestra la hoja de cálculo sin datos (figura 6.1).
Figura 6.1. La hoja de cálculo.
Cuando se selecciona una celda que contiene algún valor, este se muestra en
el campo de texto de la parte inferior (Fig. 6.2).
152
mailxmail - Cursos para compartir lo que sabes
Figura 6.2. El campo de texto muestra el valor de la celda.
Al seleccionar el comando "Menu", se despliega un menú con las opciones:
"Guardar", "Abrir", "Borrar", "Nuevo" y "Valor". (Fig. 6.3)
Figura 6.3. El menú de la hoja de cálculo.
Al tomar la opción 5 (Valor) del menú, o presionar la tecla "Select" del
emulador se ingresa a la caja de texto donde se pide que se introduzca el valor para
la celda (Fig. 6.4).
153
mailxmail - Cursos para compartir lo que sabes
Figura 6.4. Caja de texto para introducir el valor.
Cuando se elige "Guardar" en el menú, se pide el nombre del archivo en un
nuevo formulario (Fig. 6.5).
Figura 6.5. Formulario para dar nombre a un archivo.
Si lo que se ha seleccionado en el menú es la opción "Abrir" o la opción
"Borrar", se muestra una lista de archivos de donde se elegirá uno para realizar la
operación. (Figs. 6.6 y 6.7)
154
mailxmail - Cursos para compartir lo que sabes
Figura 6.6. Abrir un archivo.
Así se vería al seleccionar "Borrar";
Figura 6.7. Borrar un archivo.
Si se trataba de borrar un archivo y tal operación fue exitosa -No hay razón
para que no lo sea, puesto que sólo se muestran los archivos existentes- se muestra
un mensaje notificándolo. Este mensaje sólo dura unos segundos (Fig. 6.8).
155
mailxmail - Cursos para compartir lo que sabes
Figura 6.8. El archivo se ha borrado con éxito..
6.5. Instalación de la aplicación en un dispositivo
La instalación se ha realizado a través de una servidor web "apache", sobre la
plataforma linux, se ha utilizado también, como pasarela (gateway) entre el servidor
y el dispositivo Kennel wap gateway.
Para descargar la hoja de cálculo se ha creado una página sencilla en la cual
se colocó un enlace al archivo hoja.jad, con el teléfono conectado a Internet se
introdujo la dirección del servidor (148.246.162.16, en esa ocasión) y una vez que la
página principal se hubo abierto, se seleccionó el enlace a la aplicación. Después de
unos momentos que tardó en descargarse el archivo, la aplicación se estaba
ejecutando satisfactoriamente en el teléfono.
El primer paso ha sido configurar el servidor web según como se especifica en
el capítulo referente al tema, del libro "Linux, guía de instalación y administración",
escrito por Vicente López Camacho, además de otros siete autores, y publicado por
la editorial McGraw-Hill, en España, el año 2001.
Una vez instalado el servidor, se procedió a configurar el wap gateway, para
esto se siguieron las instrucciones detalladas en el manual de usuario del producto.
Hecho lo anterior, se copiaron los archivos de la página WML y los archivos jad
jad y jar de la aplicación para prepararlos para su descarga.
[1]Las clases Tabla y Texto están basadas en una MIDlet de ejemplo incluida en el
Java Wireless Toolkit llamada CustomItenDemo.
[2]La capacidad de utilizar múltiples pantallas (más de 6 renglones y 3
columnas) no se implementa en esta versión
156
mailxmail - Cursos para compartir lo que sabes
157
mailxmail - Cursos para compartir lo que sabes
7. Conclusión y bibliografía
7. CONCLUSIONES
De unos años a la fecha las instituciones de educación tecnológica en México,
particularmente las relacionadas con la computación y el desarrollo de software, han
centrado sus esfuerzos en un solo fin para la programación: aplicaciones de bases
de datos, principalmente para computadoras de escritorio con conexión a una red o,
en los mejores casos, para servidores de grandes compañías. Esta situación se debe,
principalmente, a lo difícil o casi imposible que se ha vuelto el competir con los
gigantes del software (Microsoft, Sun, Borland, etc.) en aplicaciones de interés
general, como pueden ser: procesadores de texto, juegos, anti virus, hojas de
cálculo, reproductores multimedia, entornos de programación y un largo etcétera;
todo el mercado de estos productos de consumo masivo se considera, en la mayoría
de las ocasiones, fuera del alcance de un estudiante de nivel licenciatura, algunas
veces de posgrado, y que decir de uno de preparatoria, por muy brillantes que estos
sean.
Si bien es cierto que para un ser humano solitario, y hasta cierto grado
principiante, es imposible competir contra un grupo enorme de profesionales que
llevan años dedicándose al desarrollo de los mismos módulos para un mismo
paquete, también lo es que ese ser humano puede llegar a formar parte de uno de
esos mitificados grupos y, por qué no, puede incluso ser el fundador de uno de ellos.
A lo largo de este documento se han podido ver las enormes capacidades de
desarrollo que los llamados "pequeños dispositivos inalámbricos" presentan, desde
la mas sencilla página web, hasta una completa aplicación con fines científicos, de
manejo de bases de datos o de acceso a la red empresarial.
Lo anterior sugiere que, siendo el campo de la programación de equipos
pequeños e inalámbricos, un campo hasta cierto punto virgen en muchas regiones
del mundo, se puede orientar a los alumnos a la creación, no ya de sistemas de
inventarios para un supermercado[1], sino de aplicaciones que, por ser de uso
general -y aunque su precio de venta sea mucho menor- resuelvan más necesidades
de más personas alrededor del mundo.
Actualmente, en los teléfonos celulares, se pueden utilizar aplicaciones como
calculadoras simples, sencillísimos editores de texto para el envío de mensajes,
158
mailxmail - Cursos para compartir lo que sabes
juegos que cada vez tienen mayor complejidad, entre otras. No hay todavía una
calculadora científica, un editor de texto con corrección de ortografía (aunque sea
por medio de la red) o un paquete de oficina compatible con los de las PC.
Se ha elegido, para este proyecto de investigación, demostrar los
conocimientos adquiridos con una aplicación de hoja de cálculo debido a la
representatividad de este tipo de paquetes, es decir, ¿Qué tipo de software puede
ser más general?. En una hoja de cálculo se puede ingresar texto, datos numéricos,
realizar operaciones, hacer programas sencillos (y en ocasiones no tan sencillos) en
las mejores hojas.
Con lo anterior se pretende enviar un mensaje a cada lector que, habiendo
pasado por las páginas anteriores, haya conservado el interés en este reporte y esté
ahora leyendo estas líneas.
"Tenemos ante nuestros ojos un campo por labrar, un campo donde quién va
pasando por un tramo se apodera de él. Es un campo enorme y, hasta ahora, son
pocos los que lo ocupan y relativamente poco el espacio que han abarcado.
Podemos dejar que suceda lo que sucedió con las computadoras de escritorio y
quedarnos sólo con el espacio donde estamos parados (y probablemente ni eso), o
podemos comenzar a ganar terreno. Quizá nunca tengamos más del espacio
suficiente para construir nuestra choza, pero es mejor que poder apenas levantar las
manos para cubrirnos de la lluvia".
8.BIBLIOGRAFÍA
1.Deitel, H.M. Deitel, P.J. "Cómo programar en Java", Prentice Hall. México.
1998.
2.Froute, Agustín, "Tutorial de Java". [email protected]. España. Julio de 1996.
3.Lopez Camacho, Vicente, "Linux, guía de instalación y administración",
McGraw-Hill, España, 2001.
4.Java.sun.com
5.www.motocoder.com
6 .www.motorola.com
7 .www.nokia.com
8 .www.sonyericsson.com
159
mailxmail - Cursos para compartir lo que sabes
8 .www.sonyericsson.com
9.www.wapforum.com
10.www.wmlclub.com
[1]Con este comentario se pretende sólo ejemplificar, por ningún motivo se
desdeña la capacidad de algunos programadores que han decidido dedicarse a las
pequeñas bases de datos.
160
Descargar