Desarrollo de aplicaciones para Black Berry Índice Herramientas de Trabajo ………………………………………………………………………… 3 Preparación del Ambiente …………………………………………………………………… 5 ……………………………………………………………………………………… 3 Primer Aplicación Base de Datos: Perst Web Services ……………………………………………………………………………… 6 …………………………………………………………………………………………………… 7 Herramientas de Trabajo 1.1 - Las herramientas de trabajo 2 Antes que nada necesitamos un JDE (Java Development Environment), o bien, el ambiente de desarollo que nos permite desarrollar las aplicaciones. 1.1.1 - El BlackBerry JDE Para nuesro desarrollo utilizaremos el plug-in para el Entorno de desarrollo de Eclipse. Para comenzar el desarrollo de una aplicacion para blackberry el primer paso sera instalar nuestro ambiente de trabajo. Para ello es necesario la instalacion de "BlackBerry JDE Plug-in para Eclipse". Para el desarrollo de este curso se utilizaran dos librerias externas: perst-jsr75.jar : Es la encargada del manejo de la base de datos orientada a objetos que se utiliza. ksoap2-j2me-core-2.1.2.jar : Se utiliza para el consumo de web services. Cabe destacar que utilizaremos como libreria del sistema Blackberry JRE 4.5.0 ya que la mayoria de los modelos actuales soportan esta libreria. 1.1.2 - El JDK Para que el BlackBerry JDE funcione necesitamos el JDK (Java Development Kit). Lo podemos descargar desde el sitio web de la Sun: http://java.sun.com/javase/downloads/index.jsp Como se muestra en la imagen, debeis seleccionar el último JDK, en éste caso el 6 update 16. Cuidado! Es posible que os encontreis nuevas actualizaciones cuando procedais al download del JDK (personalmente en mi PC tengo el update 12). Esto significa que eventualmente debereis cambiar el numero del update que vendra indicado en el argumento con aquel que hayais descargado. Después de haber elegido el JDK para descargar debereis seleccionar el Sistema 3 Operativo. Después de haber efectuado la selección del update del S.O. podremos proceder al download del archivo. Preparación del Ambiente 4 BlackBerry Java Plug-in para Eclipse es un complemento para Eclipse que mejora la productividad mediante un flujo de trabajo específico de desarrollo, depuración y simulación integrado en smartphones BlackBerry. BlackBerry Java Plug-in para Eclipse se ha actualizado para adaptarse mejor dentro del entorno de Eclipse y facilitar las tareas de desarrollo en Eclipse. Puede descargarse desde http://es.blackberry.com/developers/devbetasoftware/javaplugin.jsp Una vez completada la instalación, Eclipse se muestra con una nueva opción en la barra de menús: Desde esta opción podremos acceder a la aplicación de petición de firmas digitales o a algunas propiedades del proyecto: La configuración en Eclipse está bastante dispersa, así que el habitual menú Project>Properties de Eclipse podemos definir varias propiedades específicas de Eclipse: 5 incluyendo el tipo de aplicación: CLDC, Midlet… Y en el menú de ejecución (también en el de depuración) podemos configurar, entre otras cosas, el emulador que queremos usar con acceso a todos los parámetros del mismo: 6 El plugin encontró sin problemas todos los simuladores instalados, y también los que instalé tras el plugin. En Window->Preferences podemos configurar una opción clave: los componentes BlackBerry a usar, es decir, con qué versión del sistema vamos a compilar. El plugin encuentra tantos las instalaciones completas de JDE como las instalaciones de los componentes independientes, y enlaza la ayuda correspondiente. En esta ventana establecemos también la versión de la JDK a usar: 7 En resumen, para los que estamos acostumbrados a Eclipse programar para BlackBerry se ha vuelto una experiencia familiar, y para los que no han usado Eclipse antes, este plugin supone una alternativa que sin duda resultará más satisfactoria. Primer Aplicación En nuestra primera aplicación veremos como desplegar un mensaje en pantalla. La primera aplicación se llamara Hola Mundo. En el siguiente codigo declaramos la clase principal, primero definimos el package com.ingsoft; y despues las importaciones necesarias. La clase principal debe extender de la clase UiApplication. En el ejemplo se crea el objeto screen de la clase MiPrimerVentana y para que se muestre en la pantalla realizo pushScreen(screen); La clase MiPrimerVentana de extenderse de la clase MainSreen package com.ingsoft; import net.rim.device.api.ui.UiApplication; import javax.microedition.io.file.FileSystemRegistry; import net.rim.device.api.ui.component.Dialog; public class ClasePrincipal extends UiApplication { public static void main(String args) { 8 ClasePrincipal theApp = new ClasePrincipal(); theApp.enterEventDispatcher(); } public ClasePrincipal() { MiPrimerVenta screen = new MiPrimerVenta(Parametro1 ,ParametroN); pushScreen(screen); } } public static void errorDialog(final String message) { UiApplication.getUiApplication().invokeLater(new Runnable() { public void run() { Dialog.alert(message); } }); } } package com.ingsoft; import net.rim.device.api.ui.component.AutoTextEditField; import net.rim.device.api.ui.component.LabelField; import net.rim.device.api.ui.component.PasswordEditField; import net.rim.device.api.ui.component.SeparatorField; import net.rim.device.api.ui.container.MainScreen; public final class MiPrimerVentanai extends MainScreen { private AutoTextEditField VendedorId; private PasswordEditField VendedorClave; public MiPrimerVentana () { super(); LabelField title = new LabelField("Login", LabelField.ELLIPSIS | LabelField.USE_ALL_WIDTH); setTitle(title); add(new SeparatorField()); VendedorId = new AutoTextEditField("Vendedor: ","", 4,AutoTextEditField.EDITABLE); add(VendedorId); VendedorClave = new PasswordEditField("Clave: ","",10, PasswordEditField.EDITABLE); add(VendedorClave); } } En este ejemplo hemos creado una ventana que permite ingresar el identificador del vendedor (AutotextEditField) y la clave (PasswordEditField) del mismo. Base de Datos: Perst 9 Se ha optado por Perst como servidor de Base de datos por que las versiones de los dispositivos que se verán en el curso no soportaban SQLLite. Esta es la solución que más se acerca a lo que se necesita (y puede soportar) un teléfono móvil. Esta es una implementación en Código Abierto para J2ME de una base de datos orientada a objetos. Aunque no entraremos en detalle en materia técnica, las principales ventajas que nos puede aportar el uso de esta DB son las siguientes: ✔ Persistencia transparente y heredada ✔ Carga recursiva de objetos. ✔ Relaciones uno a uno, uno a muchos, muchos a uno y muchos a muchos. ✔ Acceso secuencial y aleatorio mediante Indices ✔ Implementaión de algoritmos eficientes para estructuras, B+Tree, T-Tree, R-Tree para búsquedas geoespaciales ✔ Posibilidad de búsquedas por valores exactos o rangos inclusivos o exclusivos ✔ Implementación de índices espaciales para búsquedas en objetos geoespaciales. ✔ Open Source También comentaremos los inconvenientes principales: ✔ Open Source. Si antes era una ventaja, hay que tener en cuenta que es una herramienta muy compleja, que no se tiene soporte técnico, y sobre todo, que no se dispone de documentación técnica. ✔ Su tamaño no es pequeño, así que habrá que sumergirse en el código en busca de material y funcionalidades que no nos sea imprescindible. Almacenamiento de objetos en la base de datos Vamos a comenzar a desarrollar una aplicación de prest. Objetivo principal prest es permitir que el programador trabajar con objetos persistentes de una manera lo más similar posible a trabajar con normalidad objetos transitorios. Sin embargo, la persistencia de ciertos aspectos que no pueden ser completamente oculto para el programador. Utilizaremos la clase StorageFactory para crear una instancia de almacenamiento: Storage db = StorageFactory.getInstance().createStorage(); Una vez que este almacenamiento de instancia se crea, se puede abrir: db.open ("test.dbs", pagePoolSize); En el ejemplo anterior, el primer parámetro especifica la ruta al archivo de base de datos, y la segundo parámetro establece el tamaño de la pool. Es posible utilizar prest como una base de datos en memoria, en la que todos los datos se almacenan y logrado en la memoria principal. Esto requiere el uso de la clase stub NullFile y especificando INFINITE_PAGE_POOL como el tamaño del pool. En este caso, el grupo de la página se ampliará en la demanda: 10 db.open (nuevo NullFile (), Storage.INFINITE_PAGE_POOL) Cuando la aplicación ha terminado de usar una base de datos prest, la base de datos debe estar cerrada. db.Close (); Root Object Después de que el almacenamiento de instancia se abre, la base de datos puede acceder a los objetos persistentes de él. Cuando la aplicación accede a normal (transitorio) los objetos, que utiliza las referencias a estos objetos conservados en las variables del programa o en campos de otros objetos. Pero cuando se abre un base de datos que contienen los objetos persistentes (objetos cuya "vida" es más largo que la aplicación sesión de la vida), la aplicación no hace referencia a estos objetos. Se necesita un mecanismo para obtener las referencias, y perst le proporciona el uso de algo denominado objeto raíz (ROOT). El almacenamiento puede tener sólo un objeto de raíz. Una vez que un objeto raíz se ha registrado, el método siempre devolverá Storage.getRoot una referencia a este objeto. Y este objeto puede contener referencias a la persistencia de otros objetos que se puede acceder por la aplicación. El código siguiente muestra la inicialización de la base de datos: MyRootClass root = (MyRootClass)db.getRoot(); if (root == null) { root = new MyRootClass(db); // create root object db.setRoot(root); // register root object } En el ejemplo se crea el root con dos indices public class Root extends Persistent { public Root() {} Index ICliente; Index ICliente1; public void writeObject(IOutputStream out) { out.writeObject(ICliente); out.writeObject(ICliente1); } public void readObject(IInputStream in) { ICliente = (Index)in.readObject(); ICliente1 = (Index)in.readObject(); } public Root(Storage db) { 11 super(db); ICliente = db.createIndex(Types.String , true); // El primer parametr "Type.String" indica que el indice cera una cadena, el segundo parametro indica si es clave o no ICliente1 = db.createIndex(Types.String , false); // En este ejemplo el primer indice es la clave primaria por el Identificador y el segundo es una clave de usuario por el nombre } } Persistencia Para que los datos se guarden en la base de datos se deben crear clases que extiendan a la clase Persistent. Esta clase permite agregar objetos a la base de datos. En el siguiente ejemplo creamos la clase cliente con sus datos (Codigo ,Nombre, Mail respectivamente) . Ademas se agregaron en la clase los metodos para agregar, modificar y eliminar un objeto. public final class Cliente extends Persistent { public Cliente(){} ////////// Datos que se guardaran en la base de datos int CliCod; String CliNom; String CliMai; //////// Metodo obligatorio para el funcionamiento public void writeObject(IOutputStream out) { out.writeInt(CliCod); out.writeString(CliNom); out.writeString(CliMai); } //////// Metodo obligatorio para el funcionamiento public void readObject(IInputStream in) { CliCod = (int)in.readInt(); CliNom = (String)in.readString(); CliMai = (String)in.readString(); } ///////// En este metodo recibimos un array con los datos del objeto public Cliente(Storage db, String row) throws NumberFormatException { super(db); CliCod = Integer.parseInt(row(0)); CliNom = row(1); CliMai = row(2); } ///////// Este metodo es similar al anterior solamente que recibe todos los datos 12 del objeto public Cliente(Storage db, int _CliCod ,String _CliNom ,String _CliMai) throws NumberFormatException { super(db); CliCod = _CliCod; CliNom = _CliNom; CliMai = _CliMai; } /////// Permite agregar un objeto a la base de datos y crea los indices , recibe como parametro el storaga y el objeto public boolean addCliente(Storage db, Cliente _Cliente) { Root root = (Root)db.getRoot(); boolean ok = root.ICliente.put(new Key(String.valueOf(_Cliente.CliCod)) , _Cliente) ; // Crea el indice primario con el codigo del cliente, puede fallar si ya existe otra objeto con la misma clave primaria if (ok) { root.ICliente1.put(new Key(String.valueOf(_Cliente.CliNom)) ,_Cliente) ; // Si logro crear el indice primario crea el indice secundario por el nombre del cliente db.commit(); // Realiza un commit y guarda los datos } return ok; } ///////// Permite actualizar un objeto y sus indices. Para hacerlo debe borrar el indice y crearlo nuevamente public boolean updCliente(Storage db,Cliente _ClienteOld ,Cliente _ClienteNew ) { Root root = (Root)db.getRoot(); root.ICliente.remove(new Key(String.valueOf(_ClienteOld.CliCod)) , _ClienteOld) ; // Borra el indice primario root.ICliente1.remove(new Key(String.valueOf(_ClienteOld.CliNom)) , _ClienteOld) ; // Borra el indice secundario boolean ok = root.ICliente.put(new Key(String.valueOf(_ClienteNew.CliId)) , _ClienteNew) ; // Crea el nuevo indice primario if (ok) { root.ICliente1.put(new Key(String.valueOf(_Cliente.CliNom)) ,_Cliente) ; // Crea el nuevo indice secundario db.commit(); // Realiza un commit } return ok; } 13 } Web Services En este capitulo veremos como consumir Web Services, que utilizan el protocolo SOAP. Se utilizara la librería KSOAP. El consumo de servicios web para móviles influye de gran manera asi se ha pensado en una solución por asi decirlo a la medida de este tipo de dispositivos y esta es kSAOP, que en cortas palabras, es la versión del protocolo SAOP para dispositivos móviles, este protocolo estándar es usado para comunicar distintas terminales a través de mensajes XML. A continuación un código para el consumo de servicios web con ksoap y j2me con apache tomcat 5 y axis: import java.io.IOException; import javax.microedition.lcdui.Form; import javax.microedition.lcdui.TextField; import org.ksoap2.SoapEnvelope; import org.ksoap2.serialization.SoapObject; import org.ksoap2.serialization.SoapSerializationEnvelope; import org.ksoap2.transport.HttpTransport; import org.xmlpull.v1.XmlPullParserException; public class Servicio extends Form implements Runnable { private String url = “http://127.0.0.1:8081/axis/Service.jws”; private TextField caja = null; private String login, password; public Servicio(String log, String pass) { super(“Prueba Servicio”); this.login=log; this.password=pass; this.caja = new TextField(“”,null,255,TextField.ANY); this.append(this.caja); Thread t = new Thread(this); t.start(); } public void run() { try { String method = “verificarDatos”; // Create the Soap Call SoapObject client = new SoapObject(url,method); HttpTransport transport = new HttpTransport(url); client.addProperty(“login”,this.login); client.addProperty(“password”,this.password); // Creating the Soap Envelope SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11); envelope.bodyOut = client; // Call the WebService try { transport.call(method, envelope); 14 } catch (XmlPullParserException ex) { ex.printStackTrace(); } String respuesta = envelope.getResponse().toString(); if (respuesta.equals(“true”)) this.caja.setString(“Validacion exitosa\nBienvenido ” + this.login); else this.caja.setString(“Validacion errada”); } catch (IOException ex) { System.out.println(ex); } } } 15