taller de programación de dispositivos móvileszeus.inf.ucv.cl/~jrubio/docs/2008-02/ici...
TRANSCRIPT
Taller de Programación de Dispositivos Móviles
José Miguel Rubio L.Oficina 3-20
http://www.inf.ucv.cl/[email protected]
Parte 11.Programación de dispositivos móviles 2.Limitaciones de los dispositivos móviles 3.Sistemas operativos móviles 4.Desarrollo de aplicaciones móviles5.Java 2 Mobile Edition6.Configuración CDC 7.Configuración CLDC 8.Paquetes opcionales en J2ME 9.MIDP: MIDlets10.MIDP: Interfaces Gráficas de Usuario 11.MIDP: Persistencia 12.MIDP: Conexión por red 13.Persistencia II: Ficheros 14.Para terminar
MIDP: Midlets
MIDP: MIDlets
●Una aplicación MIDP requiere la implementación de un MIDlet, cuya estructura recuerda los Appletsy Servlets de J2SE
import javax.microedition.midlet.MIDlet;
public class EjemploMidlet extends MIDlet
{ public void startApp() { // Arrancar aplicación
}
public void pauseApp() { // Parar aplicación }
public void destroyApp(boolean unconditional) { // Eliminar recursos
} }
Sistemas operativosmóviles
MIDP: Midlets
●De manera similar a un Applet, un MIDlet requierela implementación de tres operaciones de la clase
MIDlet: ●startApp(). Es llamada automáticamente cuando
la aplicación debe comenzar su ejecución. ●pauseApp(). El dispositivo puede solicitar la parada
temporal de la aplicación en cualquier momento.La reanudación implicará una nueva llamada astartApp() o la terminación definitiva mediante lallamada a destroyApp().
●destroyApp(). Es invocada para solicitar la liberación de los recursos del MIDlet y cualquier tareanecesaria antes de su eliminación de memoria.
Sistemas operativosmóviles
MIDP: Midlets
●Un conjunto de MIDlets se distribuye en un fichero.jar
●El MANIFEST del fichero jar es más complicado quelos que conocemos
●Primero se indica el nombre global del conjunto deMIDlets del fichero jar, su versión, autor y las versio-nes de CLDC y MIDP necesarias ●Después cada MIDlet se describe mediante un nom-bre, el icono correspondiente y el nombre de la clase que lo implementa
MIDlet-Name: EjemplosMIDPMIDlet-Version: 1.0 MIDlet-Vendor: ajruedaMicroEdition-Configuration: CLDC-1.1 MicroEdition-Profile: MIDP-2.0 MIDlet-1: Ejemplo,ejemplo.png,EjemploMidlet
Sistemas operativosmóviles
MIDP: Interfaces Gráficasde Usuario
MIDP: Interfaces Gráficas de Usuario
●MIDP proporciona una forma sencilla de construirinterfaces de usuario adaptada a las limitaciones depantalla, potencia de cálculo y batería de los dispo-sitivos móviles. ●En comparación con toolkits como Swing, la varie-dad y número de componentes existentes es muypequeño. ●La interfaz se construye a base de distintas panta-llas, en lugar de ventanas o diálogos. ●Las acciones del usuario definen la transición de una pantalla a otra .
Sistemas operativosmóviles
MIDP: Interfaces Gráficasde Usuario
●Es importante tener en cuenta el pequeño tamañola pantalla del dispositivo a la hora de diseñar lainterfaz. ●La mayoría de los dispositivos dispone de un parde botones de acción cuyo efecto se puede progra-mar para cada pantalla.
Sistemas operativosmóviles
MIDP: Interfaces Gráficasde Usuario
●Para empezar hay que obtener el objeto Displayque permite manejar la pantalla del dispositivo. ●La operación estática getDisplay() de esta clasedevuelve el objeto
Display d = Display.getDisplay() ●A continuación podemos establecer la pantalla ac-tual mediante:
d.setCurrent(Displayable pantalla) ●Una vez obtenido el display, el MIDlet sigue elsiguiente esquema de funcionamiento: 1.Crear una pantalla 2.Mostrarla mediante setCurrent() 3.Esperar las acciones del usuario 4.Elegir otra pantalla en función de estas acciones (volver a 1)
Sistemas operativosmóviles
MIDP: Interfaces Gráficasde Usuario
●Las clases que implementan la interfaz Displayable son las siguientes:
TextBox List Alert Form
Sistemas operativosmóviles Canvas
●Crear y activar una pantalla TextBox es muy sencillo:
TextBox t = new TextBox( Escribe un poema , , 500 , TextField.ANY);
d.setCurrent(t);
●Un Alert es similar a un messageBox de Swing,admitiendo distintos tipos. ●El tiempo de visualización del Alert es configurable mediante setTimeout() ●Al llamar a setCurrent() es necesario indicarel siguiente displayable a mostrar tras el Alert
Alert a = new Alert( Error , Error al salvar la in formación , null, AlertType.ERROR);
a.setTimeout(5000);
d.setCurrent(a, siguienteDisp);
móviles
MIDP: Interfaces Gráficasde Usuario
●El displayable Form permite definir una pantallacon múltiples Item (o componentes):
● StringItem. Similar a un label de Swing. ●Spacer. Un espacio con un ancho y alto determinado. Útil para distribuir los
componentes. ● TextField. Un editor de texto con una etiqueta asociada. ● ImageItem. Una imagen con una etiqueta. ● DateField. Un editor que permite introducir una fecha/hora. ● Gauge. Sirve para representar de manera gráfica un valor entero. ●ChoiceGroup. Sirve para seleccionar valores de una lista predeterminada.Puede ser múltiple, exclusiva o popup● Cualquier Item definido por el usuario
móviles
MIDP: Interfaces Gráficasde Usuario
●Los Form permiten crear interfaces mucho más ricas: Form f = new Form("Ficha deportiva"); f.append(new TextField("Apellidos", null, 40, TextF ield.ANY)); f.append(new TextField("Nombre", null, 40, TextFiel d.ANY)); f.append(new DateField("Fecha de nacimiento", DateFi eld.DATE)); f.append(new TextField("E-mail", null, 20, TextField.EMAILADDR)); String[] tipos = {"Profesor", "Alumno"}; f.append(cg = new ChoiceGroup("Tipo", ChoiceGroup.EX CLUSIVE, tipos, null));
d.setCurrent(f);
Sistemas operativosmóviles
●Para asociar acciones a los botones del dispositivose utiliza la clase Command
●Las activación de un comando es capturada porun CommandListener, cuya única operación es
commandAction() class listenerTextBox implements CommandListener { public commandAction(Command cm, Displayable ds) { if (cm == cFin) {
// Procesar el comando }
} }
TextBox t = new TextBox( Escribe un poema , , 500 , TextField.ANY);
t.addCommand(cFin = new Command( Fin , Command.OK, 0)); t.setListener(new listenerTextBox());
d.setCurrent(t);
Tarea:Siguiendo el ejemplo que hemos desarrollado en la asignatura, crear unvisor móvil de nuestra cuenta
import javax.microedition.midlet.*; import javax.microedition.lcdui.*; import java.util.*;
public class MidletCuenta extends MIDlet{ private Cuenta c;
private Display d; private TextField tCodigo; private Command cEntrar, cSalir, cVolver, cMovimiento s;
private Form fPeticionCodigo, fInfoCuenta; private List lMovCuenta;
public MidletCuenta() { d = null;
}
void crearComandos() { cEntrar = new Command("Entrar", Command.OK, 0); cSal ir = new Command("Salir", Command.EXIT, 1); cVolver = newCommand("Volver", Command.BACK, 0); cMovimientos = newCommand("ver Mov.", Command.SCREEN, 0); }
MIDP: Interfaces Gráficasde Usuario
public void startApp() { if (d == null) { d = Display.getDisplay(this); } crearComandos();
crearFormPeticionCodigo(); d.setCurrent(fPeticionCodigo); }
void crearFormPeticionCodigo() { fPeticionCodigo = new Form("Información de cuentas"); fPeticionCodigo.append(tCodigo = new TextField("Códig o de cuenta ", null, 10, TextField.NUMERIC)); fPeticionCodigo.addCommand(cEntrar); fPeticionCodigo.addCommand(cSalir);
fPeticionCodigo.setCommandListener(new listenerPetic ionCodigo()); tCodigo.setLayout(Item.LAYOUT_2 | Item.LAYOUT_CENT ER); }
class listenerPeticionCodigo implements CommandListene r { public void commandAction(Command cm, Displayable s) { if(cm == cEntrar) {
// Cargar la cuenta aquí
crearFormInformacionCuenta(); d.setCurrent(fInfoCuenta); } elseif (cm == cSalir) { destroyApp(true);
notifyDestroyed(); }
} }
Sistemas operativosmóviles
MIDP: Interfaces Gráficasde Usuario
void crearFormInformacionCuenta() { // Crear formulario de información dae la cuenta fIn foCuenta = newForm("Información de cuenta"); fInfoCuenta.append(n ewStringItem("Código:", Long.toString(c.leerNumero()) ));
fInfoCuenta.append(new StringItem("Titular:", c.leer Titular()));
fInfoCuenta.append(new StringItem("Interés:", Float.toString(c.leerInteres()))); fInfoCuenta.appe nd(newStringItem("Saldo:", Float.toString(c.leerSaldo())) );
fInfoCuenta.addCommand(cMovimientos); fInfoCuenta.addCommand(cVolver); fInfoCuenta.setCommandListener(new listenerInformaci onCuenta());
// Crear lista de movimientos lMovCuenta = new List("Ultimos movimientos", List.IMPLICIT); Movimiento m; int nMov, nm; for (nMov = 10, nm = c.numMovimientosHistorico() - 1;
nMov > 0 && nm >= 0; nm--, nMov--) { m = c.leerMovimientoHistorico(nm); lMovCuenta.append(cadenaDate(m.fecha) + ' ' + m.t ipo + '
' + m.importe + ' ' + m.saldo, null); }
lMovCuenta.addCommand(cVolver); lMovCuenta.setCommandListener(new listenerInformacio nMovimientos());
}
Sistemas operativosmóviles
MIDP: Interfaces Gráficasde Usuario
class listenerInformacionCuenta implements CommandList ener{ public void commandAction(Command cm, Displayable s) { if(cm == cVolver) {
d.setCurrent(fPeticionCodigo); } else if (cm == cMovimientos) { d.setCurrent(lMovCuenta); }
} }
class listenerInformacionMovimientos implements Comman dListener { public void commandAction(Command cm, Displayable s) {
if (cm == cVolver) { d.setCurrent(fInfoCuenta);
} }
}
Sistemas operativosmóviles
MIDP: Persistencia
MIDP: Persistencia
●La capacidad de almacenamiento persistente de undispositivo móvil puede ser muy limitada.
●El soporte de tarjetas de memoria, cada vez máscomún, ha aumentado mucho las prestaciones (8Gben iPhone o Nokia N95) posibilitando una estructurade ficheros similar a la de un computador convencional. ●Sin embargo el perfil MIDP es conservador y pro-porciona un soporte sencillo de persistencia a través de registros de bloques de bytes.
Sistemas operativosmóviles
MIDP: Persistencia
●Un MIDlet puede abrir un almacén de registroscon un nombre arbitrario mediante:
RecordStore RecordStore.openRecordStore(“nombre”, true); ●El segundo parámetro indica que el almacéndebe abrirse si no existe. ●A través de las operaciones del objeto RecordStore podremos manejar los registros. ●Normalmente un almacén no se comparte conotros MIDlets, aunque puede habilitarse este acceso. ●El almacén de registros se cierra mediante:
closeRecordStore()
Sistemas operativosmóviles
MIDP: Persistencia
●El acceso a los registros se realiza a través de un identificador numérico, que es devuelto alañadir un registro:
int addRecord(byte[] datos, int offset, int numBytes) ●Para recuperar un registro debemos indicar suidentificador:
int getRecord(int id, byte[] buffer, int offset) ●Modificar un registro ya existente:
void setRecord(int id, byte[] nuevosDatos, int offset, int numBytes) ●Eliminar un registro:
void deleteRecord(int id)
Sistemas operativosmóviles
MIDP: Persistencia
●Es posible recorrer los registros de un almacéncreando una enumeración:
RecordStore rs = RecordStore.openRecordStores( ejem plo, true);
RecordEnumeration re = re.enumerateRecords(null, nu ll, false); while (re.hasNextElement()) {
byte[] datos = re.nextRecord(); // Operar con los datos
}
re.destroy(); rs.closeRecordStore();
●La operación enumerateRecords() admite la es-pecificación de clases de filtrado y ordenación de los registros del almacén.
Sistemas operativosmóviles
MIDP: Persistencia
Tarea:Crear un gestor de persistencia para las cuentas corrientes medianteun almacén de registros.
import java.io.*; import java.util.*; import javax.microedition.rms.*;
public class DAOCuentaRS { static DAOCuentaRS instancia = null;
public static DAOCuentaRS obtenerInstancia() throws RecordStoreException{ if (instancia == null) {
instancia = new DAOCuentaRS(); } return instancia;
}
private DAOCuentaRS() {}
public boolean existe(long numero) throws RecordStore Exception, IOException{ RecordStore rs = null; try { rs = RecordStore.openRecordStore("cuentas", true); return (buscarRegistroCuenta(rs, numero) != -1); } finally {
if (rs != null) rs.closeRecordStore(); }
}
Sistemas operativosmóviles
MIDP: Persistencia
public Cuenta cargar(long numero) throws RecordStoreE xception, IOException{ RecordStore rs = null; DataInputStream dis = null; Cuenta c = null;
try { rs = RecordStore.openRecordStore("cuentas", true);
int idReg = buscarRegistroCuenta(rs, numero); if (idReg == -1) {
return null; }
dis = new DataInputStream(newByteArrayInputStream(rs.getRecord(idReg))); c = ne wCuenta(dis.readLong(), dis.readUTF(), dis.readFloat ()); c.saldo = dis.readFloat(); int nMov = dis.readInt(); for (int nm = 0; nm < nMov; nm++) { c.movimientos.addElement(new Movimiento(new Date(dis. readLong()), dis.readChar(), dis.readFloat(), dis.readFloat())); }
} finally { if (dis != null) dis.close(); if (rs!= null) rs.closeRecordStore(); }
return c; }
Sistemas operativosmóviles
MIDP: Persistencia
public void salvar(Cuenta c) throws RecordStoreExceptio n, IOException{ RecordStore rs = null; ByteArrayOutputStream bos = null; DataOutputStream dos = null; try {
rs = RecordStore.openRecordStore("cuentas", true); d os = newDataOutputStream(bos = new ByteArrayOutputStream());
dos.writeLong(c.leerNumero()); dos.writeUTF(c.leerT itular()); dos.writeFloat(c.leerInteres()); dos.writeFloat(c.l eerSaldo());
Movimiento m; int nMov = c.numMovimientosHistorico(); dos.writeInt( nMov);
for (int nm = 0; nm < nMov; nm++) { m = c.leerMovimientoHistorico(nm);
dos.writeLong(m.fecha.getTime()); dos.writeChar(m.t ipo); dos.writeFloat(m.importe); dos.writeFloat(m.saldo); }
int idReg = buscarRegistroCuenta(rs, c.leerNumero()); if (idReg != -1) {
rs.setRecord(idReg, bos.toByteArray(), 0, bos.size()); } else {
rs.addRecord(bos.toByteArray(), 0, bos.size()); }
} finally { if (dos != null) dos.close(); if (rs!= null) rs.closeRecordStore(); }
} Sistemas operativos
móviles
MIDP: Persistencia
private int buscarRegistroCuenta(RecordStore rs, long numero) throws RecordStoreException, IOException { RecordEnumeration re = null; DataInputStream dis = null; long recNum; int id;
try { re = rs.enumerateRecords(null, null, false); while (re.hasNextElement()) {
id = re.nextRecordId(); dis = new DataInputStream(newByteArrayInputStream(rs.getRecord(id))); recNum = di s.readLong(); if (recNum == numero)
{ return id; } dis.close(); dis = null;
} } finally { if (dis != null) dis.close(); if (re != null) re.destroy();
}
return -1; }
}
Sistemas operativosmóviles
MIDP: Persistencia
En el MIDlet debemos crear un atributo para referenciar el gestor de persistencia, y realizar su inicialización en startApp()
public void startApp() { if (d == null) {
d = Display.getDisplay(this); }
if (dc == null) { try {
dc = DAOCuentaRS.obtenerInstancia(); } catch(Exception e) {
d.setCurrent(new Alert("Error", "No es posible abrir el almacén de registros", null, AlertType.ERROR));
destroyApp(true); notifyDestroyed(); return;
} }
// Crear las cuentas de ejemplo si no existen crearCuentasEjemplo();
crearComandos(); crearFormPeticionCodigo(); d.setCurrent(fPeticionCodigo); }
Sistemas operativosmóviles
MIDP: Persistencia
class listenerPeticionCodigo implements CommandListene r { public void commandAction(Command cm, Displayable s) { if(cm == cEntrar) {
try { c = dc.cargar(Long.parseLong(tCodigo.getString ())); if (c == null) {
d.setCurrent(new Alert("Error", "Cuenta inexistente" , null, AlertType.ERROR), fPeticionCodigo); return;
} } catch(Exception e) {
d.setCurrent(new Alert("Error", "Error de lectura de cuenta", null, AlertType.ERROR), fPeticionCodigo);
return; }
crearFormInformacionCuenta(); d.setCurrent(fInfoCuenta); } elseif (cm == cSalir) { destroyApp(true);
notifyDestroyed(); }
} }
Sistemas operativosmóviles
MIDP: Conexión por red
MIDP: Conexión por red
●MIDP es especialmente potente en lo que se refiere a la conexión por red mediante sockets, httpy otros protocolos ●La clase Connection representa una conexión genérica y es extendida a tres conexiones que admiten E/S mediante streams: InputConnection, OutputConnection y StreamConnection●La clase StreamConnection es extendida a varias clases que representan distintos tipos de conexiones: CommConnection, HttpConnection, httpsConnection, SocketConnection, etc.
Sistemas operativosmóviles