Taller de Programación
de Dispositivos Móviles

              José Miguel Rubio L.
                       Oficina 3-20
       http://www.inf.ucv.cl/~jrubio
                jose.rubio.l@ucv.cl
Parte 1
1.Programación de dispositivos móviles
2.Limitaciones de los dispositivos móviles
3.Sistemas operativos móviles
4.Desarrollo de aplicaciones móviles
5.Java 2 Mobile Edition
6.Configuración CDC
7.Configuración CLDC
8.Paquetes opcionales en J2ME
9.MIDP: MIDlets
10.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 Applets
y 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 operativos
                                                                         móviles
MIDP: Midlets




De manera similar a un Applet, un MIDlet requiere
●

la 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 a
  startApp() o la terminación definitiva mediante la
  llamada a destroyApp().
●destroyApp(). Es invocada para solicitar la liberación

  de los recursos del MIDlet y cualquier tarea
  necesaria antes de su eliminación de memoria.
                                             Sistemas operativos
                                                        móviles
MIDP: Midlets




●Un conjunto de MIDlets se distribuye en un fichero
 .jar
●El MANIFEST del fichero jar es más complicado que

  los que conocemos
●Primero se indica el nombre global del conjunto de

MIDlets 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: EjemplosMIDP
           MIDlet-Version:    1.0
           MIDlet-Vendor: ajrueda
           MicroEdition-Configuration: CLDC-1.1
           MicroEdition-Profile: MIDP-2.0
           MIDlet-1: Ejemplo,ejemplo.png,EjemploMidlet

                                                         Sistemas operativos
                                                                    móviles
MIDP: Interfaces Gráficas
                                                           de Usuario




MIDP: Interfaces Gráficas de Usuario
● MIDP proporciona una forma sencilla de construir
interfaces de usuario adaptada a las limitaciones de
pantalla, 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 muy
pequeñ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 operativos
                                                          móviles
MIDP: Interfaces Gráficas
                                                         de Usuario




●Es importante tener en cuenta el pequeño tamaño
la pantalla del dispositivo a la hora de diseñar la
interfaz.
●La mayoría de los dispositivos dispone de un par

de botones de acción cuyo efecto se puede progra-
mar para cada pantalla.




                                             Sistemas operativos
                                                        móviles
MIDP: Interfaces Gráficas
                                                                                      de Usuario




●Para empezar hay que obtener el objeto Display
que permite manejar la pantalla del dispositivo.
●La operación estática getDisplay() de esta clase

 devuelve 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 el

 siguiente 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 operativos
                                                                                     móviles
MIDP: Interfaces Gráficas
                                                            de Usuario




●Las clases que implementan la interfaz Display
able son las siguientes:




     TextBox      List            Alert   Form




                                                 Sistemas operativos
                         Canvas                             móviles
●   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 indicar

el siguiente displayable a mostrar tras el Alert
        Alert a = new Alert(    Error ,    Error al salvar la información   ,
          null, AlertType.ERROR);
        a.setTimeout(5000);

        d.setCurrent(a, siguienteDisp);


                                                                                      móviles
MIDP: Interfaces Gráficas
                                                                                    de Usuario




El displayable Form permite definir una pantalla
●

con 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áficas
                                                                                      de Usuario




●   Los Form permiten crear interfaces mucho más ricas:
    Form f = new Form("Ficha deportiva");
    f.append(new TextField("Apellidos", null, 40, TextField.ANY));
    f.append(new TextField("Nombre", null, 40, TextField.ANY));
    f.append(new DateField("Fecha de nacimiento", DateField.DATE));
    f.append(new TextField("E-mail", null, 20,
    TextField.EMAILADDR));
    String[] tipos = {"Profesor", "Alumno"};
    f.append(cg = new ChoiceGroup("Tipo", ChoiceGroup.EXCLUSIVE, tipos, null));


    d.setCurrent(f);




                                                                          Sistemas operativos
                                                                                     móviles
●Para asociar acciones a los botones del dispositivo
  se utiliza la clase Command
●Las activación de un comando es capturada por

 un 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 un
visor 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, cMovimientos;

           private Form fPeticionCodigo, fInfoCuenta;
           private List lMovCuenta;

           public    MidletCuenta()
               { d   = null;
           }

           void crearComandos() {
           cEntrar = new Command("Entrar", Command.OK, 0); cSalir    =
           new Command("Salir", Command.EXIT, 1); cVolver = new
           Command("Volver", Command.BACK, 0); cMovimientos = new
           Command("ver Mov.", Command.SCREEN, 0); }
MIDP: Interfaces Gráficas
                                                                               de 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ódigo de cuenta
    ", null, 10, TextField.NUMERIC));
    fPeticionCodigo.addCommand(cEntrar);
    fPeticionCodigo.addCommand(cSalir);
fPeticionCodigo.setCommandListener(new listenerPeticionCodigo());
tCodigo.setLayout(Item.LAYOUT_2 | Item.LAYOUT_CENTER); }


class listenerPeticionCodigo implements CommandListener {
public void commandAction(Command cm, Displayable s) { if
(cm == cEntrar) {
            // Cargar la cuenta aquí

        crearFormInformacionCuenta();
        d.setCurrent(fInfoCuenta); } else
        if (cm == cSalir) {
        destroyApp(true);
            notifyDestroyed();
        }
    }
}

                                                                   Sistemas operativos
                                                                              móviles
MIDP: Interfaces Gráficas
                                                                                  de Usuario


void crearFormInformacionCuenta() {
    // Crear formulario de información dae la cuenta fInfoCuenta    = new
    Form("Información de cuenta"); fInfoCuenta.append(new
    StringItem("Código:", Long.toString(c.leerNumero())));

    fInfoCuenta.append(new StringItem("Titular:", c.leerTitular()));

    fInfoCuenta.append(new StringItem("Interés:",
    Float.toString(c.leerInteres()))); fInfoCuenta.append(new
    StringItem("Saldo:", Float.toString(c.leerSaldo())));


    fInfoCuenta.addCommand(cMovimientos);
    fInfoCuenta.addCommand(cVolver);
    fInfoCuenta.setCommandListener(new listenerInformacionCuenta());

    // 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.tipo + '
          ' + m.importe + ' ' + m.saldo, null);
    }

    lMovCuenta.addCommand(cVolver);
    lMovCuenta.setCommandListener(new listenerInformacionMovimientos());
}


                                                                      Sistemas operativos
                                                                                 móviles
MIDP: Interfaces Gráficas
                                                                                 de Usuario


class listenerInformacionCuenta implements CommandListener
{ public void commandAction(Command cm, Displayable s) { if
(cm == cVolver) {
            d.setCurrent(fPeticionCodigo);
        }
        else if (cm == cMovimientos)
        { d.setCurrent(lMovCuenta); }

    }
}

class listenerInformacionMovimientos implements CommandListener   {
    public void commandAction(Command cm, Displayable s) {
        if (cm == cVolver) {
            d.setCurrent(fInfoCuenta);
        }
    }
}




                                                                      Sistemas operativos
                                                                                 móviles
MIDP: Persistencia




MIDP: Persistencia
●La capacidad de almacenamiento persistente de un
 dispositivo móvil puede ser muy limitada.
●El soporte de tarjetas de memoria, cada vez más

común, ha aumentado mucho las prestaciones (8Gb
en iPhone o Nokia N95) posibilitando una estructura
de ficheros similar a la de un computador convencio
nal.
●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 operativos
                                                        móviles
MIDP: Persistencia




Un MIDlet puede abrir un almacén de registros
●

con un nombre arbitrario mediante:
        RecordStore RecordStore.openRecordStore(“nombre”, true);
● El segundo parámetro indica que el almacén
debe abrirse si no existe.
●A través de las operaciones del objeto Record

Store podremos manejar los registros.
●Normalmente   un almacén no se comparte con
otros MIDlets, aunque puede habilitarse este acceso
.
●El almacén de registros se cierra mediante:

                        closeRecordStore()




                                                               Sistemas operativos
                                                                          móviles
MIDP: Persistencia




●El acceso a los registros se realiza a través de
un identificador numérico, que es devuelto al aña-
dir un registro:
             int addRecord(byte[] datos, int offset, int numBytes)
●Para recuperar un registro debemos indicar su
identificador:
                   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 operativos
                                                                                        móviles
MIDP: Persistencia




Es posible recorrer los registros de un almacén
●

creando una enumeración:
    RecordStore rs   = RecordStore.openRecordStores(   ejemplo, true);

    RecordEnumeration re    = re.enumerateRecords(null, null, 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 operativos
                                                                                    móviles
MIDP: Persistencia




Tarea:Crear un gestor de persistencia para las cuentas corrientes mediante
un 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 RecordStoreException, IOException
   { RecordStore rs = null;
   try {
   rs = RecordStore.openRecordStore("cuentas", true);
   return (buscarRegistroCuenta(rs, numero) != -1); }
   finally {
       if (rs != null) rs.closeRecordStore();
   }
 }


                                                                    Sistemas operativos
                                                                               móviles
MIDP: Persistencia



public Cuenta cargar(long numero) throws RecordStoreException, 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(new
          ByteArrayInputStream(rs.getRecord(idReg))); c = new
          Cuenta(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 operativos
                                                                                   móviles
MIDP: Persistencia

public void salvar(Cuenta c) throws RecordStoreException, IOException
  { RecordStore rs = null;
  ByteArrayOutputStream   bos    =
  null; DataOutputStream dos     =
  null;
  try {
      rs = RecordStore.openRecordStore("cuentas", true); dos = new
      DataOutputStream(bos = new ByteArrayOutputStream());

       dos.writeLong(c.leerNumero());    dos.writeUTF(c.leerTitular());
       dos.writeFloat(c.leerInteres()); dos.writeFloat(c.leerSaldo());

       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.tipo);
       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(new
              ByteArrayInputStream(rs.getRecord(id))); recNum   = dis.readLong();
              if (recNum == numero)
                  { return id;
              }
              dis.close();
              dis = null;
          }
        } finally {
        if (dis != null) dis.close();
        if (re != null) re.destroy();
                                       }

        return   -1;
    }
}




                                                                          Sistemas operativos
                                                                                     móviles
MIDP: Persistencia



  En el MIDlet debemos crear un atributo para referenciar el gestor de persis
-tencia, 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 operativos
                                                                                móviles
MIDP: Persistencia




class listenerPeticionCodigo implements CommandListener {
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); } else
        if (cm == cSalir) {
        destroyApp(true);
            notifyDestroyed();
        }
    }
}




                                                                   Sistemas operativos
                                                                              móviles
MIDP: Conexión por red




MIDP: Conexión por red
●MIDP es especialmente potente en lo que se refie-
re a la conexión por red mediante sockets, http y
otros protocolos
●La clase Connection representa una conexión ge-

nérica y es extendida a tres conexiones que admi-
ten E/S mediante streams: InputConnection, Output-
Connection y StreamConnection
●La clase StreamConnection es extendida a varias

clases que representan distintos tipos de conexiones:
 CommConnection, HttpConnection, httpsConnection,
SocketConnection, etc.
                                              Sistemas operativos
                                                         móviles
MIDP: Conexión por red




●La clase Connector es una factoría que a partir d
e un url devuelve la clase de conexión correspon-
diente:     Connection Connector.open(String url)
HttpConnection con1;
con1 = (HttpConnection) Connector.open(http://www.google.es/search?hl=es&q=j2me);

SocketConnection con2;
con2 = (SocketConnection) Connector.open(socket://miservidor:79);



●La conexión debe cerrarse al final con close()
●A partir de la conexión podemos obtener un stream

de lectura o escritura
HttpConnection con1;
con1 = (HttpConnection) Connector.open(http://www.google.es/search?hl=es&q=j2me);

InputStream is = con1.openInputStream();
// Utilizar el stream
con1.close();



                                                                      Sistemas operativos
                                                                                 móviles
MIDP: Conexión por red




 Ejemplo:El siguiente gestor de persistencia obtiene los datos de la cuenta
desde un servidor web

import   java.io.*;
import java.util.*;
import      javax.microedition.io.*;
import javax.microedition.io.file.*;

public class DAOCuentaNC {
  static DAOCuentaNC instancia   = null;

  public static DAOCuentaNC obtenerInstancia()
    { if (instancia == null) {
        instancia = new DAOCuentaNC();
    }
    return instancia;
  }

  private   DAOCuentaNC()
  { }

  public boolean existe(long     numero)
    { try {
      cargar(numero);
    }
    catch(Exception   e)
      { return false;
    }
    return true;
  }
                                                                     Sistemas operativos
                                                                                móviles
MIDP: Conexión por red



    public Cuenta cargar(long numero) throws IOException
      { InputConnection ic = null;
      DataInputStream   dis    =
      null; Cuenta c = null;

        try     {
              ic    =    (InputConnection) Connector.open(urlCuenta(numero)); dis      =
              ic.openDataInputStream();
              c   = new Cuenta(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())); }

        } catch(Exception e) { // No se
            encuentra la cuenta return
            null;
        } finally {
          if (ic != null) ic.close();
        }

        return c;
    }

    private String urlCuenta(long codigo) {
        return    "http://robin.ujaen.es/asignaturas/progav/cuentas/"
           + Long.toString(codigo) + ".cnt";
    }
}

                                                                             Sistemas operativos
                                                                                        móviles
Persistencia II: Ficheros




Persistencia II: Ficheros
●El File Connection and PIM API (JSR 75) define
un nuevo tipo de conexión denominado FileConnec-
tion que permite trabajar con ficheros de manera
similar a un computador convencional.
●Este API no está disponible en todos los disposi-

tivos.
●El acceso al sistema de ficheros requiere permisos

especiales si la aplicación no está certificada, para
evitar daños en el mismo.


                                               Sistemas operativos
                                                          móviles
Persistencia II: Ficheros




    El siguiente ejemplo lee una imagen guardada en el dispositivo
FileConnection fc;
InputStream is;

fc = (FileConnection) Connector.open( /Imagenes/flower.jpg   , Connector.READ);
is = fc.openInputStream();
Image mi      = Image.createImage(is); //
Utilizar la imagen

is.close();




●La clase FileConnection permite abrir un stream de
E/S al fichero pero también contiene operaciones
para la creación, renombrado y borrado de ficheros
 y directorios.



                                                                        Sistemas operativos
                                                                                   móviles
Persistencia II: Ficheros




●No obstante existe un problema importante: la e
structura del sistema de ficheros de cada disposit
ivo no es estándar
●Cada dispositivo contiene una raíz para cada un

o de los medios de almacenamiento: memoria int
erna (/root, /internal, /InternalMemory) y tarjetas d
e memoria (/SDCard1, /MemoryCard)
●Es posible enumerar las distintas raices existe

ntes mediante el registro del sistema de ficher
os:
Enumeration     raicesSF            =
FileSystemRegistry.listRoots();             while
(raicesSF.hasMoreElements()) {
raizSF = (String) raicesSF.nextElement();
// Hacer algo con la raiz encontrada }




                                                       Sistemas operativos
                                                                  móviles
Persistencia II: Ficheros




Este gestor de persistencia utiliza el API JSR 75
import java.io.*;
import java.util.*;
import      javax.microedition.io.*;
import javax.microedition.io.file.*;

class FileConnectionAPIInexistente extends Exception   {}

public class DAOCuentaFC {
  static DAOCuentaFC instancia         =
  null; String raizSF;

 public static DAOCuentaFC obtenerInstancia() throws FileConnectionAPIInexistente
   { if (instancia == null) {
       String versionFCAPI = System.getProperty(
       "microedition.io.file.FileConnection.version");
       if (versionFCAPI == null) {
           throw new FileConnectionAPIInexistente();
       }
       instancia = new DAOCuentaFC();
   }
   return instancia;
 }

 private DAOCuentaFC() {
 obtenerRaizSistemaFicheros(); }




                                                                     Sistemas operativos
                                                                                móviles
Persistencia II: Ficheros




public boolean existe(long    numero)
  { try {
    cargar(numero);
  }
  catch(Exception   e)
    { return false;
  }
  return true;
}

public Cuenta cargar(long numero) throws IOException
  { FileConnection fc = null;
  DataInputStream   dis    =
  null; Cuenta c = null;

 try     {
       fc        =        (FileConnection)  Connector.open(urlCuenta(numero),
       Connector.READ); if (!fc.exists()) {
           return null;
       }

       dis = fc.openDataInputStream();
       c         =     new    Cuenta(dis.readLong(),   dis.readUTF(),
       dis.readFloat()); c.saldo = dis.readFloat();




                                                                        Sistemas operativos
                                                                                   móviles
Persistencia II: Ficheros


          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 (fc != null) fc.close();
    }

    return c;
}

public void salvar(Cuenta c) throws IOException
  { FileConnection fc = null;
  DataOutputStream dos = null;

    try     {
          fc     =    (FileConnection) Connector.open("file:///"   +
             raizSF + "cuentas", Connector.READ_WRITE);
          if (!fc.exists()) {
               fc.mkdir();
          }
          fc.close();


          fc                       =                     (FileConnection)
            Connector.open(urlCuenta(c.leerNumero()),
          ifConnector.READ_WRITE);
               (!fc.exists())
              { fc.create();
          }

                                                                            Sistemas operativos
                                                                                       móviles
Persistencia II: Ficheros
         dos   = fc.openDataOutputStream();

         dos.writeLong(c.leerNumero());
         dos.writeUTF(c.leerTitular());
         dos.writeFloat(c.leerInteres());
         dos.writeFloat(c.leerSaldo());

         Movimiento m;
         int           nMov                      =
         c.numMovimientosHistorico();
         dos.writeInt(nMov); nm < nMov; nm++)
         for (int nm = 0;
             {           m                     =
             c.leerMovimientoHistorico(nm);
             dos.writeLong(m.fecha.getTime());
             dos.writeChar(m.tipo);
         dos.writeFloat(m.importe);
         dos.writeFloat(m.saldo); }

        } finally {
            if (fc != null) fc.close();
        }
    }

    private String urlCuenta(long codigo) {
    return "file:///" + raizSF + "cuentas/"    +
    Long.toString(codigo) + ".cnt"; }


    private void obtenerRaizSistemaFicheros() { Enumeration
        raicesSF = FileSystemRegistry.listRoots(); if
        (raicesSF.hasMoreElements()) {
            raizSF = (String) raicesSF.nextElement();
        }
    }
}                                                                Sistemas operativos
                                                                            móviles
Persistencia II: Ficheros




●Otro problema adicional son las restricciones de
seguridad existentes en algunos dispositivos, que
sólo permiten el acceso a determinados directorio
s públicos (/Imagenes, /Sonidos, etc.)




                                              Sistemas operativos
                                                         móviles
Para terminar




Para terminar
●Hemos estudiado MIDP a nivel básico
●En función de las necesidades de la aplicación

móvil a implementar será necesario estudiar con
mayor profundidad algunos de los APIs que hem
os visto y otros nuevos
  ● PIM API JSR 75

  ● API MIDP para juegos


    ●

    Mobile Media API JSR JSR 184
    ●

    Wireless Messaging API JSR 13
    5 ● Bluetooth API JSR 82               Sistemas operativos
                                                      móviles

Taller de programación

  • 1.
    Taller de Programación deDispositivos Móviles José Miguel Rubio L. Oficina 3-20 http://www.inf.ucv.cl/~jrubio jose.rubio.l@ucv.cl
  • 2.
    Parte 1 1.Programación dedispositivos móviles 2.Limitaciones de los dispositivos móviles 3.Sistemas operativos móviles 4.Desarrollo de aplicaciones móviles 5.Java 2 Mobile Edition 6.Configuración CDC 7.Configuración CLDC 8.Paquetes opcionales en J2ME 9.MIDP: MIDlets 10.MIDP: Interfaces Gráficas de Usuario 11.MIDP: Persistencia 12.MIDP: Conexión por red 13.Persistencia II: Ficheros 14.Para terminar
  • 3.
    MIDP: Midlets MIDP: MIDlets ●Unaaplicación MIDP requiere la implementación de un MIDlet, cuya estructura recuerda los Applets y 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 operativos móviles
  • 4.
    MIDP: Midlets De manerasimilar a un Applet, un MIDlet requiere ● la 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 a startApp() o la terminación definitiva mediante la llamada a destroyApp(). ●destroyApp(). Es invocada para solicitar la liberación de los recursos del MIDlet y cualquier tarea necesaria antes de su eliminación de memoria. Sistemas operativos móviles
  • 5.
    MIDP: Midlets ●Un conjuntode MIDlets se distribuye en un fichero .jar ●El MANIFEST del fichero jar es más complicado que los que conocemos ●Primero se indica el nombre global del conjunto de MIDlets 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: EjemplosMIDP MIDlet-Version: 1.0 MIDlet-Vendor: ajrueda MicroEdition-Configuration: CLDC-1.1 MicroEdition-Profile: MIDP-2.0 MIDlet-1: Ejemplo,ejemplo.png,EjemploMidlet Sistemas operativos móviles
  • 6.
    MIDP: Interfaces Gráficas de Usuario MIDP: Interfaces Gráficas de Usuario ● MIDP proporciona una forma sencilla de construir interfaces de usuario adaptada a las limitaciones de pantalla, 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 muy pequeñ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 operativos móviles
  • 7.
    MIDP: Interfaces Gráficas de Usuario ●Es importante tener en cuenta el pequeño tamaño la pantalla del dispositivo a la hora de diseñar la interfaz. ●La mayoría de los dispositivos dispone de un par de botones de acción cuyo efecto se puede progra- mar para cada pantalla. Sistemas operativos móviles
  • 8.
    MIDP: Interfaces Gráficas de Usuario ●Para empezar hay que obtener el objeto Display que permite manejar la pantalla del dispositivo. ●La operación estática getDisplay() de esta clase devuelve 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 el siguiente 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 operativos móviles
  • 9.
    MIDP: Interfaces Gráficas de Usuario ●Las clases que implementan la interfaz Display able son las siguientes: TextBox List Alert Form Sistemas operativos Canvas móviles
  • 10.
    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 indicar el siguiente displayable a mostrar tras el Alert Alert a = new Alert( Error , Error al salvar la información , null, AlertType.ERROR); a.setTimeout(5000); d.setCurrent(a, siguienteDisp); móviles
  • 11.
    MIDP: Interfaces Gráficas de Usuario El displayable Form permite definir una pantalla ● con 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
  • 12.
    MIDP: Interfaces Gráficas de Usuario ● Los Form permiten crear interfaces mucho más ricas: Form f = new Form("Ficha deportiva"); f.append(new TextField("Apellidos", null, 40, TextField.ANY)); f.append(new TextField("Nombre", null, 40, TextField.ANY)); f.append(new DateField("Fecha de nacimiento", DateField.DATE)); f.append(new TextField("E-mail", null, 20, TextField.EMAILADDR)); String[] tipos = {"Profesor", "Alumno"}; f.append(cg = new ChoiceGroup("Tipo", ChoiceGroup.EXCLUSIVE, tipos, null)); d.setCurrent(f); Sistemas operativos móviles
  • 13.
    ●Para asociar accionesa los botones del dispositivo se utiliza la clase Command ●Las activación de un comando es capturada por un 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);
  • 14.
    Tarea:Siguiendo el ejemploque hemos desarrollado en la asignatura, crear un visor 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, cMovimientos; private Form fPeticionCodigo, fInfoCuenta; private List lMovCuenta; public MidletCuenta() { d = null; } void crearComandos() { cEntrar = new Command("Entrar", Command.OK, 0); cSalir = new Command("Salir", Command.EXIT, 1); cVolver = new Command("Volver", Command.BACK, 0); cMovimientos = new Command("ver Mov.", Command.SCREEN, 0); }
  • 15.
    MIDP: Interfaces Gráficas de 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ódigo de cuenta ", null, 10, TextField.NUMERIC)); fPeticionCodigo.addCommand(cEntrar); fPeticionCodigo.addCommand(cSalir); fPeticionCodigo.setCommandListener(new listenerPeticionCodigo()); tCodigo.setLayout(Item.LAYOUT_2 | Item.LAYOUT_CENTER); } class listenerPeticionCodigo implements CommandListener { public void commandAction(Command cm, Displayable s) { if (cm == cEntrar) { // Cargar la cuenta aquí crearFormInformacionCuenta(); d.setCurrent(fInfoCuenta); } else if (cm == cSalir) { destroyApp(true); notifyDestroyed(); } } } Sistemas operativos móviles
  • 16.
    MIDP: Interfaces Gráficas de Usuario void crearFormInformacionCuenta() { // Crear formulario de información dae la cuenta fInfoCuenta = new Form("Información de cuenta"); fInfoCuenta.append(new StringItem("Código:", Long.toString(c.leerNumero()))); fInfoCuenta.append(new StringItem("Titular:", c.leerTitular())); fInfoCuenta.append(new StringItem("Interés:", Float.toString(c.leerInteres()))); fInfoCuenta.append(new StringItem("Saldo:", Float.toString(c.leerSaldo()))); fInfoCuenta.addCommand(cMovimientos); fInfoCuenta.addCommand(cVolver); fInfoCuenta.setCommandListener(new listenerInformacionCuenta()); // 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.tipo + ' ' + m.importe + ' ' + m.saldo, null); } lMovCuenta.addCommand(cVolver); lMovCuenta.setCommandListener(new listenerInformacionMovimientos()); } Sistemas operativos móviles
  • 17.
    MIDP: Interfaces Gráficas de Usuario class listenerInformacionCuenta implements CommandListener { public void commandAction(Command cm, Displayable s) { if (cm == cVolver) { d.setCurrent(fPeticionCodigo); } else if (cm == cMovimientos) { d.setCurrent(lMovCuenta); } } } class listenerInformacionMovimientos implements CommandListener { public void commandAction(Command cm, Displayable s) { if (cm == cVolver) { d.setCurrent(fInfoCuenta); } } } Sistemas operativos móviles
  • 18.
    MIDP: Persistencia MIDP: Persistencia ●Lacapacidad de almacenamiento persistente de un dispositivo móvil puede ser muy limitada. ●El soporte de tarjetas de memoria, cada vez más común, ha aumentado mucho las prestaciones (8Gb en iPhone o Nokia N95) posibilitando una estructura de ficheros similar a la de un computador convencio nal. ●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 operativos móviles
  • 19.
    MIDP: Persistencia Un MIDletpuede abrir un almacén de registros ● con un nombre arbitrario mediante: RecordStore RecordStore.openRecordStore(“nombre”, true); ● El segundo parámetro indica que el almacén debe abrirse si no existe. ●A través de las operaciones del objeto Record Store podremos manejar los registros. ●Normalmente un almacén no se comparte con otros MIDlets, aunque puede habilitarse este acceso . ●El almacén de registros se cierra mediante: closeRecordStore() Sistemas operativos móviles
  • 20.
    MIDP: Persistencia ●El accesoa los registros se realiza a través de un identificador numérico, que es devuelto al aña- dir un registro: int addRecord(byte[] datos, int offset, int numBytes) ●Para recuperar un registro debemos indicar su identificador: 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 operativos móviles
  • 21.
    MIDP: Persistencia Es posiblerecorrer los registros de un almacén ● creando una enumeración: RecordStore rs = RecordStore.openRecordStores( ejemplo, true); RecordEnumeration re = re.enumerateRecords(null, null, 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 operativos móviles
  • 22.
    MIDP: Persistencia Tarea:Crear ungestor de persistencia para las cuentas corrientes mediante un 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 RecordStoreException, IOException { RecordStore rs = null; try { rs = RecordStore.openRecordStore("cuentas", true); return (buscarRegistroCuenta(rs, numero) != -1); } finally { if (rs != null) rs.closeRecordStore(); } } Sistemas operativos móviles
  • 23.
    MIDP: Persistencia public Cuentacargar(long numero) throws RecordStoreException, 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(new ByteArrayInputStream(rs.getRecord(idReg))); c = new Cuenta(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 operativos móviles
  • 24.
    MIDP: Persistencia public voidsalvar(Cuenta c) throws RecordStoreException, IOException { RecordStore rs = null; ByteArrayOutputStream bos = null; DataOutputStream dos = null; try { rs = RecordStore.openRecordStore("cuentas", true); dos = new DataOutputStream(bos = new ByteArrayOutputStream()); dos.writeLong(c.leerNumero()); dos.writeUTF(c.leerTitular()); dos.writeFloat(c.leerInteres()); dos.writeFloat(c.leerSaldo()); 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.tipo); 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
  • 25.
    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(new ByteArrayInputStream(rs.getRecord(id))); recNum = dis.readLong(); if (recNum == numero) { return id; } dis.close(); dis = null; } } finally { if (dis != null) dis.close(); if (re != null) re.destroy(); } return -1; } } Sistemas operativos móviles
  • 26.
    MIDP: Persistencia En el MIDlet debemos crear un atributo para referenciar el gestor de persis -tencia, 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 operativos móviles
  • 27.
    MIDP: Persistencia class listenerPeticionCodigoimplements CommandListener { 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); } else if (cm == cSalir) { destroyApp(true); notifyDestroyed(); } } } Sistemas operativos móviles
  • 28.
    MIDP: Conexión porred MIDP: Conexión por red ●MIDP es especialmente potente en lo que se refie- re a la conexión por red mediante sockets, http y otros protocolos ●La clase Connection representa una conexión ge- nérica y es extendida a tres conexiones que admi- ten E/S mediante streams: InputConnection, Output- Connection y StreamConnection ●La clase StreamConnection es extendida a varias clases que representan distintos tipos de conexiones: CommConnection, HttpConnection, httpsConnection, SocketConnection, etc. Sistemas operativos móviles
  • 29.
    MIDP: Conexión porred ●La clase Connector es una factoría que a partir d e un url devuelve la clase de conexión correspon- diente: Connection Connector.open(String url) HttpConnection con1; con1 = (HttpConnection) Connector.open(http://www.google.es/search?hl=es&q=j2me); SocketConnection con2; con2 = (SocketConnection) Connector.open(socket://miservidor:79); ●La conexión debe cerrarse al final con close() ●A partir de la conexión podemos obtener un stream de lectura o escritura HttpConnection con1; con1 = (HttpConnection) Connector.open(http://www.google.es/search?hl=es&q=j2me); InputStream is = con1.openInputStream(); // Utilizar el stream con1.close(); Sistemas operativos móviles
  • 30.
    MIDP: Conexión porred Ejemplo:El siguiente gestor de persistencia obtiene los datos de la cuenta desde un servidor web import java.io.*; import java.util.*; import javax.microedition.io.*; import javax.microedition.io.file.*; public class DAOCuentaNC { static DAOCuentaNC instancia = null; public static DAOCuentaNC obtenerInstancia() { if (instancia == null) { instancia = new DAOCuentaNC(); } return instancia; } private DAOCuentaNC() { } public boolean existe(long numero) { try { cargar(numero); } catch(Exception e) { return false; } return true; } Sistemas operativos móviles
  • 31.
    MIDP: Conexión porred public Cuenta cargar(long numero) throws IOException { InputConnection ic = null; DataInputStream dis = null; Cuenta c = null; try { ic = (InputConnection) Connector.open(urlCuenta(numero)); dis = ic.openDataInputStream(); c = new Cuenta(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())); } } catch(Exception e) { // No se encuentra la cuenta return null; } finally { if (ic != null) ic.close(); } return c; } private String urlCuenta(long codigo) { return "http://robin.ujaen.es/asignaturas/progav/cuentas/" + Long.toString(codigo) + ".cnt"; } } Sistemas operativos móviles
  • 32.
    Persistencia II: Ficheros PersistenciaII: Ficheros ●El File Connection and PIM API (JSR 75) define un nuevo tipo de conexión denominado FileConnec- tion que permite trabajar con ficheros de manera similar a un computador convencional. ●Este API no está disponible en todos los disposi- tivos. ●El acceso al sistema de ficheros requiere permisos especiales si la aplicación no está certificada, para evitar daños en el mismo. Sistemas operativos móviles
  • 33.
    Persistencia II: Ficheros El siguiente ejemplo lee una imagen guardada en el dispositivo FileConnection fc; InputStream is; fc = (FileConnection) Connector.open( /Imagenes/flower.jpg , Connector.READ); is = fc.openInputStream(); Image mi = Image.createImage(is); // Utilizar la imagen is.close(); ●La clase FileConnection permite abrir un stream de E/S al fichero pero también contiene operaciones para la creación, renombrado y borrado de ficheros y directorios. Sistemas operativos móviles
  • 34.
    Persistencia II: Ficheros ●Noobstante existe un problema importante: la e structura del sistema de ficheros de cada disposit ivo no es estándar ●Cada dispositivo contiene una raíz para cada un o de los medios de almacenamiento: memoria int erna (/root, /internal, /InternalMemory) y tarjetas d e memoria (/SDCard1, /MemoryCard) ●Es posible enumerar las distintas raices existe ntes mediante el registro del sistema de ficher os: Enumeration raicesSF = FileSystemRegistry.listRoots(); while (raicesSF.hasMoreElements()) { raizSF = (String) raicesSF.nextElement(); // Hacer algo con la raiz encontrada } Sistemas operativos móviles
  • 35.
    Persistencia II: Ficheros Estegestor de persistencia utiliza el API JSR 75 import java.io.*; import java.util.*; import javax.microedition.io.*; import javax.microedition.io.file.*; class FileConnectionAPIInexistente extends Exception {} public class DAOCuentaFC { static DAOCuentaFC instancia = null; String raizSF; public static DAOCuentaFC obtenerInstancia() throws FileConnectionAPIInexistente { if (instancia == null) { String versionFCAPI = System.getProperty( "microedition.io.file.FileConnection.version"); if (versionFCAPI == null) { throw new FileConnectionAPIInexistente(); } instancia = new DAOCuentaFC(); } return instancia; } private DAOCuentaFC() { obtenerRaizSistemaFicheros(); } Sistemas operativos móviles
  • 36.
    Persistencia II: Ficheros publicboolean existe(long numero) { try { cargar(numero); } catch(Exception e) { return false; } return true; } public Cuenta cargar(long numero) throws IOException { FileConnection fc = null; DataInputStream dis = null; Cuenta c = null; try { fc = (FileConnection) Connector.open(urlCuenta(numero), Connector.READ); if (!fc.exists()) { return null; } dis = fc.openDataInputStream(); c = new Cuenta(dis.readLong(), dis.readUTF(), dis.readFloat()); c.saldo = dis.readFloat(); Sistemas operativos móviles
  • 37.
    Persistencia II: Ficheros 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 (fc != null) fc.close(); } return c; } public void salvar(Cuenta c) throws IOException { FileConnection fc = null; DataOutputStream dos = null; try { fc = (FileConnection) Connector.open("file:///" + raizSF + "cuentas", Connector.READ_WRITE); if (!fc.exists()) { fc.mkdir(); } fc.close(); fc = (FileConnection) Connector.open(urlCuenta(c.leerNumero()), ifConnector.READ_WRITE); (!fc.exists()) { fc.create(); } Sistemas operativos móviles
  • 38.
    Persistencia II: Ficheros dos = fc.openDataOutputStream(); dos.writeLong(c.leerNumero()); dos.writeUTF(c.leerTitular()); dos.writeFloat(c.leerInteres()); dos.writeFloat(c.leerSaldo()); Movimiento m; int nMov = c.numMovimientosHistorico(); dos.writeInt(nMov); nm < nMov; nm++) for (int nm = 0; { m = c.leerMovimientoHistorico(nm); dos.writeLong(m.fecha.getTime()); dos.writeChar(m.tipo); dos.writeFloat(m.importe); dos.writeFloat(m.saldo); } } finally { if (fc != null) fc.close(); } } private String urlCuenta(long codigo) { return "file:///" + raizSF + "cuentas/" + Long.toString(codigo) + ".cnt"; } private void obtenerRaizSistemaFicheros() { Enumeration raicesSF = FileSystemRegistry.listRoots(); if (raicesSF.hasMoreElements()) { raizSF = (String) raicesSF.nextElement(); } } } Sistemas operativos móviles
  • 39.
    Persistencia II: Ficheros ●Otroproblema adicional son las restricciones de seguridad existentes en algunos dispositivos, que sólo permiten el acceso a determinados directorio s públicos (/Imagenes, /Sonidos, etc.) Sistemas operativos móviles
  • 40.
    Para terminar Para terminar ●Hemosestudiado MIDP a nivel básico ●En función de las necesidades de la aplicación móvil a implementar será necesario estudiar con mayor profundidad algunos de los APIs que hem os visto y otros nuevos ● PIM API JSR 75 ● API MIDP para juegos ● Mobile Media API JSR JSR 184 ● Wireless Messaging API JSR 13 5 ● Bluetooth API JSR 82 Sistemas operativos móviles