L'arquitectura Java ME té els següents components:
![]() |
![]() |
La màquina virtual és la capa més baixa de l'arquitectura Java ME i per tant és la més propera al maquinari. A diferència del que succeeix en ordinadors personals, les màquines virtuals en dispositius mòbils venen proporcionades pel fabricant del dispositiu, és a dir, estan incorporades en el dispositiu.
En Java ME, ens trobem amb 2 màquines virtuals principals, segons el tipus de configuració:
Consulteu:
http://ca.wikipedia.org/wiki/CLDC
Consulteu:
http://ca.wikipedia.org/wiki/CDC_(informàtica)
Consulteu:
http://ca.wikipedia.org/wiki/MIDP
El perfil Information Module Profile (definit al JSR 195) està dissenyat per a màquines d'auto-vending, targetes de xarxa, encaminadors o altres dispositius amb una pantalla molt simple o sense pantalla i que tenen connexió limitada a xarxa.
Els creadors d'aquesta especificació van ser Siemens Mobile i Nokia.
Aquest perfil va ser dissenyat per als mòbils i-mode de DoCoMo
Consulteu l'article Netbeans i Java ME
Consulteu Sun_Java_Wireless_Toolkit
Supported Toolkit List
http://eclipseme.org/docs/support_wtk.html
Per a una definició de MIDlet, consulteu l'article de la viquipèdia [Connected Limited Device Configuration MIDlet].
Un MIDlet té el diagrama d'estats que podeu veure a la dreta, amb 3 estats:
Quan volem crear un MIDlet hem de crear una nova classe Java que hereti de la classe abstracta:
javax.microedition.midlet.MIDlet
L'esquelet d'un MIDlet és el següent:
import javax.microedition.midlet.*; public class MiMIDlet extends MIDlet { protected void startApp() throws MIDletStateChangeException { // Mou el MIDlet a l'estat actiu -> iniciar } protected void pauseApp() { // Mou el MIDlet a l'estat pausa -> pausar } protected void destroyApp(boolean incondicional) throws MIDletStateChangeException { // Mou el MIDlet a l'estat destruït -> allibera recursos } }
La interfície gràfica amb CLDC es realitza amb l'API LCDUI que forma part del perfil MIDP. Cal tenir en compte que hi ha 2 versions de MIDP:
LCDUI vol dir Limited Connected Devices User Interface, i representa el mateix per a Java ME que les interfícies Swing i AWT per a Java SE.
El paquet que conté aquesta API és javax.microedition.lcdui i podeu consultar els javadocs online a:
http://java.sun.com/javame/reference/apis.jsp#api
O amb Netbeans els trobareu a:
~/netbeans-6.5/mobility8/WTK2.5.2/docs/api
La classe Display representa una pantalla de mòbil. Aquest objecte permet accedir a la pantalla i al teclat. Cal tenir en compte que cada MIDlet només té associat un sol Display. Es pot obtenir el Display amb:
Display display = Display.getDisplay(midlet);
Es pot emmagatzemar el Display en un Bean Pattern. Per exemple el codi Hola Mon! de Netbeans utilitza el següent codi:
public Display getDisplay () { return Display.getDisplay(this); }
En una pantalla (Display) es poden mostrar components gràfics també anomenats Displayables per què tots són extensió (hereten) de la classe abstracta Displayable.
Només es pot mostrar un component gràfic a l'hora i els components gràfics estan dividits en 2 nivells lògics:
Es determina quin és el component gràfic a mostrar per pantalla en cada moment amb:
Display.setCurrent ( displayable);
Els components d'alt nivell hereten de la classe abstracta Screen (buida) que a l'hora hereta de Displayable. Cada component pot tenir:
La interfície gràfica sovint es pot desenvolupar mitjançant eines RAD. Amb Netbeans, les interfícies gràfiques, es poden implementar utilitzant la pestanya Flow:
Consulteu l'exemple: [Projecte_Java_ME_GestioAules#1r_Exercici._Creaci.C3.B3_de_la_interf.C3.ADcie_gr.C3.A0fica_d.27usuari_d.27alt_nivell.LCDUI._Versi.C3.B3_0.1 Projecte Gestió Aules], concretament l'exercici 1, per tal de veure un exemple d'aplicació implementada amb Netbeans i LCDUI.
Es poden incloure imatges en molt components gràfics. Les imatges s'encapsulen a la classe Image i l'únic format reconegut per l'estàndard és PNG. Es poden obtenir a partir de fitxer continguts dins del fitxer JAR:
Image img = Image.createImage(“/logo.png”);
L'entrada dels usuaris es realitza mitjançant comandes (classe Command)
Per afegir una comanda a un component gràfic:
TextBox tb = new TextBox("Login", "", 8, TextField.ANY); Command cmdOK = new Command("OK", Command.OK, 1); Command cmdAyuda = new Command("Ajuda", Command.HELP, 1); Command cmdSalir = new Command("Sortir", Command.EXIT, 1); Command cmdBorrar = new Command("Esborrar", Command.SCREEN, 1); tb.addCommand(cmdOK); tb.addCommand(cmdAyuda); tb.addCommand(cmdSalir); tb.addCommand(cmdBorrar); Display d = Display.getDisplay(this); d.setCurrent(tb);
En Java per tractar els esdeveniments s'utilitzen Listeners que és un component que escolta un determinat esdeveniment. Quan succeïx l'esdeveniment s'executa el codi del Listener. Hi ha 3 tipus de Listeners a Java ME:
El MIDlet ha d'implementar la classe Listener desitjada. Cal implementar els mètodes de la interfície i Registrar el Listener al component gràfic el qual ha de rebre els esdeveniments
public class HelloMIDlet extends MIDlet implements CommandListener {
L'exemple Hola Mon! de Netbeans:
public class HelloMIDlet extends MIDlet implements CommandListener { ... public void commandAction(Command command, Displayable displayable) { // write pre-action user code here if (displayable == form) { if (command == exitCommand) { // write pre-action user code here exitMIDlet(); // write post-action user code here } } // write post-action user code here } public Form getForm() { if (form == null) { // write pre-init user code here form = new Form("Welcome", new Item[] { getStringItem() }); form.addCommand(getExitCommand()); form.setCommandListener(this); // write post-init user code here } return form; }
MIDP 1.0
MIDP 2.0:
A Java ME, el sistema de dibuix es basa en un objecte anomenat Canvas. Canvas vol dir llenç (la superfície sobre la qual un pintor pinta). El procés de pintat sobre un llenç és molt similar al procés de pintat amb AWT o Swing. La idea bàsica és que l'objecte Canvas proporciona una àrea de dibuix que és accedida cada cop que el mètode paint() és invocat. Les tasques de dibuix són realitzades per una classe anomenada Graphics. Aquesta classe proporciona mètodes primitius per a dibuixar sobre el llenç.
Cal tenir en compte que la Classe llenç és abstracta i, per tant, cal implementar una classe filla de canvas que implementi el mètode paint():
protected abstract void paint(Graphics g)
Per exemple:
public class MyCanvas extends Canvas { ... public void paint(Graphics g) { //Aquí posem el codi per pintar al Canvas } ... }
Recursos:
L'objecte Graphics proporciona primitives de dibuix. Es poden pintar:
Totes les primitives poden ser pintades amb línia sòlides (SOLID) o puntejades (DOTTED). El tipus de línia a utilitzar es modifica amb el mètode setStrokeStyle().
Totes les operacions de dibuix es duen a terme en un espai de 2 dimensions amb coordenades (X,Y) on la coordenada (0,0) és el cantó superior-esquerre de la pantalla.
public void translate(int x, int y)
També es pot treballar amb Imatges i text:
Per pintar imatges i text s'utilitzen anchor points que permeten col·locar les imatges i el text en posicions molt concretes de la pantalla. Per exemple:
g.drawString(mMessage, mWidth / 2, mHeight, Graphics.BOTTOM | Graphics.HCENTER);
Es poden combinar és valors (amb |):
Mostra un missatge a la part inferior centrada de la pantalla.
Colors:
El color s'estableix amb un Model_de_color_RGB (red,green,blue). Disposem dels mètodes:
public void setColor(int red, int green, int blue); public void setColor(int RGB); public void setGrayScale(int value);
per tal d'establir el color que s'està utilitzant per pintar.
Els colors es codifiquen en 24 bits. Cal tenir en compte però que ens podem trobar amb dispositius amb limitacions:
Es recomana com a bona pràctica, comprovar el tipus de dispositiu abans de començar a utilitzar colors. Hi ha una sèrie de mètodes de la classe Display que ens permeten comprovar les limitacions del dispositiu:
En qualsevol moment podeu consultar el color actual amb:
public int getColor()
O el valor de gris amb:
public int getGrayScale()
També es pot accedir directament als component del color RGB (red,green,blue) amb els mètodes:
public int getBlueComponent(); public int getGreenComponent(); public int getRedComponent();
Fonts
L'objecte Font controla el tipus de fonts que es poden utilitzar. Hi han diversos mètodes per dibuixar text:
public void drawString(String str, int x, int y, int anchor) public void drawSubstring(String str, int offset, int len, int x, int y, int anchor) public void drawChar(char character, int x, int y, int anchor)
La font actual és consulta amb:
public Font getFont();
I s'estableix amb:
public void setFont(Font font);
Veiem un exemple:
font = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_LARGE);
Fixeu-vos que no s'indica un tipus de font concret sinó unes característiques. El mòbil retornarà la font més similar de la que disposi.
Tenim mètodes que ens permeten consultar l'alçada de la font i així pode escriure diverses línies sense xocar. Un exemple:
height = f.getHeight(); for (i = 0; i < txt.length; i++) { g.drawString(txt[i], x, y, TOP|LEFT); y += height; }
El mètode paint és cridat pel propi mòbil cada cop que necessita tornar a mostrar la pantalla. Per tant, és el MIDlet qui controla quan s'ha de tornar a dibuixar la pantalla.
Però en alguns cassos ens interessarà tornar a dibuixar la pantalla en moment concrets de temps. Alguns exemples:
Cal tenir en compte que la instància de l'objecte Graphics que és utilitzat pel mètode paint, només és vàlida durant la execució del mètode paint. Per tant, no podem accedir a l'objecte Graphics, modificar-lo i cridar aleshores el mètode paint, per que les modificacions que hem fet a l'objecte Graphics no s'aplicaran. De fet el mètode paint no és mai cridat directament pel MIDlet.
Per tornar a pintar la pantalla cal utilitzar el mètode repaint(). Aquest mètode no crida el mètode paint() directament sinó que demana a l'entorn d'execució que cridi al mètode paint.
Veiem un exemple de com podem mostrar un missatge per pantalla amb repaint:
public class MyCanvas extends Canvas { ... private String mMessage=null; ... public void setMessage(String s) { mMessage = s; repaint(); } public void paint(Graphics g) { // Mostrar un missatge si és que hi ha un missatge if (mMessage != null) g.drawString(mMessage, mWidth / 2, mHeight, Graphics.BOTTOM | Graphics.HCENTER); ... } ... }
Com ja hem comentat, podem utilitzar el mètode repaint per tal de crear una animació. Cal destacar però, que si l'animació és complexa és possible que per problemes de rendiment noteu un efecte indesitjable de parpelleig. Per evitar aquest efecte indesitjable t és comú utilitzar la tècnica anomenada double-buffering.
NOTA: Cal tenir en compte que hi ha dispositius que ja suporten per defecte double-buffering
Amb MIDP és fàcil implementar el double-buffering. La classe Image pot ser utilitzada per a crear una imatge off-screen amb el mètode createImage(width, height). Un cop has obtingut la imatge, es pot cridar el mètode getGraphics(), per tal d'obtenir una instància de l'objecte Graphics, que pot ser utilitzar per a pintar en la imatge de la mateixa forma que ho faríem al mètode paint per tal de pintar directament a la pantalla. Un cop teniu la imatge preparada, aquesta es pot pintar per pantalla utilitzant el mètode Graphics.drawImage().
Exemple:
private Image offscreen; ... offscreen = Image.createImage(getWidth(), getHeight()); g = offscreen.getGraphics(); paint(g); ... public void paint(Graphics g) { ... originalG = g; g = offscreen.getGraphics(); ... }
Control d'esdeveniments de teclat (Keystrokes)
L'objecte Canvas pot controlar esdeveniments de teclat amb els mètodes
protected void keyPressed(int keyCode) protected void keyReleased(int keyCode) protected void keyRepeated(int keyCode)
Podeu sobrescriure aquests mètodes a les classes filles de Canvas que utilitzeu. Els control d'esdeveniments per defecte d'aquests mètodes és no fer res.
Disposem d'una sèrie de constants predefinides (variables static i final de la classe Canvas):
Aquests codis són codis estàndards de la ITU-T. Poden existir altres tecles i altres codis en certs dispositius però si es vol garantir la portabilitat de l'aplicació només podem utilitzar les tecles estàndard.
Accions de joc (game actions)
En comptes d'utilitzar els codis específics de tecles com les fletxes de direcció, les aplicacions Java portables han d'utilitzar accions de joc (game actions).
A MIDP es defineixen les següents accions de joc:
Cada codi de tecla està associat com a màxim a una acció de joc però una acció de jo pot estar associada a més d'un codi de joc.
Disposem dels mètodes:
getGameAction(int keyCode) getKeyCode(int gameAction)
Per relacionar codis de tecla amb accions de joc.
Per tant tingueu em compte que:
g == getGameAction(getKeyCode(g)) // (1) k == getKeyCode(getGameAction(k)) // (2)
La expressió 1 sempre serà vàlida. No podem dir el mateix de la expressió (2).
Cal tenir en compte que per exemple les tecles de fletxa poden ser especifiques de cada dispositiu, i que alguns dispositius sense tecles de fletxa potser retornen els codis de les tecles 2,4,6 i 8 com a tecles de fletxa.
Idea!: Un menú inicial amb Canvas que permeti accedir a les opcions de menú directament pressionant un número.
Esdeveniments de punter
Cal que tenir en compte que no tots els mòbils suporten esdeveniments de punter
Els mètodes que controlen els esdeveniments de punter són:
protected void pointerDragged(int x, int y) protected void pointerPressed(int x, int y) protected void pointerReleased(int x, int y)
Podeu comprovar si el mòbil suporta esdeveniments de punter amb els mètodes:
public boolean hasPointerEvents()
public boolean hasPointerMotionEvents()
El segon mètode comprova si el mòbil permet controlar drags (arrossegament) del punter.
L'API de jocs (paquet javax.microedition.lcdui.game) proveeix de 5 classes:
Esquelet d'un Joc Java ME:
public class MyCanvas extends Canvas implements Runnable { public void run() { while(true) { repaint(); // update the game state } public void paint(Graphics g) { // code for painting } protected void keyPressed(int keyCode) { // respond to key events } }
Recursos:
L'objecte GameCanvas és una subclasse de l'objecte javax.microedition.lcdui.Canvas. També és una classe abstracta.
GameCanvas proporciona les següents ajudes per al desenvolupament de jocs:
GameCanvas simplifica la programació de jocs ja que tota la funcionalitat del joc es pot controlar en un sol bucle, sota el control d'un únic fil d'execució. L'esquelet d'un joc fet amb Game Canvas és:
public class MyCanvas extends GameCanvas implements Runnable { public void run() { Graphics g = getGraphics(); while(true) { // update the game state // ... int k = getKeyStates(); // respond to key events flushGraphics(); } } }
La classe abstracta Layer representa un element visual d'un joc, com un Sprite o un TiledLayer.
Els atributs d'una capa són:
Les capes es poden superposar entre si. Les capes poder ser visibles o invisibles. En el cas de superposició la capa superior és la que es mostrarà (si és visible).
Totes les capes han d'implementar el mètode paint(Graphics g), que és l'encarregat de mostrar el contingut gràfic de la capa.
Sprite
Els jocs 2D típics tenen un personatge que es mou al voltant d'un fons gràfic i que interactua amb l'entorn. Un exemple clar és el típic joc dels "comecocos" on una boca es va moment per un laberint, menjant cocos mentres evita els fantasmes.
En programació de jocs, el conjunt de diferents imatges d'aquests personatges són anomenades sprites (en català follets). La classe Sprite representa una imatge gràfica a on s'emmagatzemen totes les possibles imatges d'un sprite. Un sprite es divideix en diverses imatges que anomenen marcs (frames)
Les propietats d'un sprite són:
Tots els frames d'un sprite es proporcionen en una sola imatge (objecte Image). L'ordre dels frames en la imatge pot ser de diferents formes. Us mostrem uns quants exemples d'sprites:
Recursos:
TiledLayer
Les capes de rajola (la paraula Tile vol dir rajola en català) són similars als Sprites però tenen un objectiu diferent. S'utilitzen per a crear fons de jocs.
A diferència d'un sprite, un TiledLayer no té transformacions, ni seqüències de marcs ni pixels de referència.Una capa de rajoles és una graella formada per un conjunt de marcs. És similar a un terra de ceràmica, format per un conjunt de rajoles.
El conjunt de rajoles es guarda en una sola imatge, on tots els Tiles (rajoles) tenen la mateixa mida:
Igual que passa amb els sprites es poden guardar el conjunt de rajoles en diferents formes dins d'una imatge.
A cada rajola se li assigna un identificador únic. Per construir un fons de pantalla, s'utilitza una matriu on a cada posició s'indica l'identificador de la rajola a mostrar en aquella posició:
Es poden incloure objectes animats, fent que algunes rajoles tinguin diferents imatges possibles.
Permet gestionar les capes d'un joc. L'ordre en que es pinten les capes és l'invers de l'ordre en que s'afegeixen les capes al gestor de capes.
Organització típica
També permet definir quina és la vista (la zona visible) d'una imatge en cada moment.A aquesta zona visible també se l'anomena view port. Aquesta funcionalitat ens permet treballar amb imatges grans i anar “navegant” per la imatge
LayerManager.setViewWindow(int x, int y, int width, int height)
Els dispositius amb suport per a CDC disposen d'accés a gairebé tota la API de Java SE. Això inclou suport per a JDBC, i per tant es pot utilitzar accés a base de dades.
En canvi, els dispositius CLDC, no disposen de JDBC. Només s'ofereix un sistema primitiu de base de dades anomenat Record Managment System (RMS).
Alguns dispositius, poden implementar la API opcional JSR 75 (File Connection) que permet accedir a fitxers. En aquest cas però, cal provar com funciona el programa en diferents dispositius, ja que aquesta API és opcional (alguns dispositius no la proporcionaran...) i pot no funcionar igual en tots els dispositius.
Record Management System (RMS) és el sistema que permet emmagatzemar dades de forma persistent amb Java ME i el perfil MIDP. El paquet és javax.microedition.rms.
Cal destacar les següents característiques:
Un altre cop, els que sapigueu desenvolupar aplicacions Java, oblideu-vos del suport de base de dades de Java SE. No podem utilitzar JDBC amb mòbils.
Un magatzem inclou diversos registres. Cada registre conté:
RecordStore rs = RecordStore.open(nomMagatzem, true);
rs.close();
String [] nomsMagatzems = RecordStore.listRecordStores();
RecordStore.deleteRecordStore(nomMagatzem);
RecordEnumeration re = rs.enumerateRecords(null, null, false); while(re.hasNextElement()) { int id = re.nextRecordId(); byte [] datos = rs.getRecord(id); // Processar les dades ... }
RecordStore rs; ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); try { dos.writeUTF(“http://www.upc.edu”); dos.writeInt(50) } catch (IOException e) { e.printStackTrace(); } byte [] barrURL = baos.toByteArray(); //Afegir les dades al registre try { rs = RecordStore.openRecordStore(this.PreferencesRecordStore, true); rs.addRecord(barrURL, 0, barrURL.length); rs.closeRecordStore(); baos.close(); dos.close(); } catch (Exception e) { e.printStackTrace(); }
byte [] dades = rs.getRecord(id); ByteArrayInputStream bais = new ByteArrayInputStream(dades); DataInputStream dis = DataInputStream(bais); String nom = dis.readUTF(); String edat = dis.readInt();
rs.deleteRecord(id);
GCF col dir Generic Connection Framework i és una API que proporciona MIDP per a les connexions remotes de xarxa. El paquet que proporciona aquesta API és:
javax.microedition.io
És un sistema dissenyat per establir connexions amb independència del tipus de xarxa (commutació de circuits, commutació de paquets, sense fils, etc.)
Existeix un mètode (open) i una classe (Connector) genèrics de connexió:
Connection con = Connector.open(url);
IMPORTANT: Les URL no són només pàgines web!
Exemples de URL:
Les classes de l'API GCF tenen una estructura jeràrquica com la següent:
Com podeu veure CLDC només implementa connexions genèriques i és MIDP qui implementa les connexions HTTP. HTTP és l'únic protocol que podem està segurs que tot mòbil que suporti Java ME suportarà. En API opcionals es poden implementar altres tipus de connexions (p. ex. Bluetooth).
L'únic protocol al qual s'assegura suport en tots els mòbils amb Java ME és HTTP i sempre funcionarà igual amb independència del tipus de xarxa subjacent (WAP, iMODE, 3G, Wifi TCP/IP).
Per obrir una connexió amb el protocol HTTP:
HttpConnection con = (HttpConnection) Connector.open(URL);
Un cop tenim una connexió, podem obrir un flux d'entrada amb les classes que implementen InputConnection:
InputStream is = con.openInputStream();
o
DataInputStream dis = con.openDataInputStream()
Ara ja podem processar el flux de la connexió. El que fem en aquest punt depèn de cada aplicació però un cop hem acabat amb la connexió cal tancar-la:
in.close(); con.close();
La diferència entre DataInputStream i InputStream és que el primer té mètodes que ens permeten accedir a tipus de dades Java:
Amb InputStream, en de treballar les dades en cru (tenim un stream de bytes).
La resta de mètodes de HTTPConnection permeten controlar el protocol HTTP. La connexió HTTP pot estar en un de 3 estats possibles:
Podem controlar tant les peticions com les respostes del protocol HTTP i també les capçaleres:
Requests: Request Methods suportats:
La resta de HTTP no estan suportats (PUT, DELETE...). Podem establir el mètode amb:
con.setRequestMethod(HttpConnection.GET);
Cal recordar que això cal executar-ho abans de començar a enviar o rebre dades de la connexió (estat setup de la connexió).
Podem afegir capçaleres a la petició HTTP amb:
con.setRequestProperty(nom, valor);
Per exemple, a vegades és interessant indicar al servidor web al que ens connectem, quin tipus de client (navegador) utilitzem. Per a aplicacions Java ME seria:
con.setRequestProperty("User-Agent","Profile/MIDP-1.0 Configuration/CLDC-1.0");
Response:
Podem obtenir el codi d'estat de la connexió HTTP (Status Codes) i el missatge corresponent amb:
int codi = con.getResponseCode(); String misatge = con.getResponseMessage();
I podem obtenir qualsevol capçalera amb:
String valor = con.getHeaderField(nomCapçalera);
Per a les capçaleres més importants, tenim mètodes específics:
Recursos:
L'ús de d'execució fils d'execució en Java ME és una qüestió força habitual, per no dir necessària. Els fils s'utilitzen per deslligar l'execució del model de la nostra aplicació de l'execució de l'entorn gràfic. Qualsevol aplicació es pot classificar en 3 parts:
Per a més informació podeu consultar a la wikipedia l'article Model Vista Controlador.
Al programar amb entorns mòbils, una de les normes a seguir és que tot el codi que executem en els controladors d'esdeveniments, a de ser codi que s'executi de forma ràpida de manera que no bloquegi l'execució de la interfície gràfica.
La solució a aquest problema és utilitzar fils d'execució. Aquelles tasques que requereixin de cert temps per executar-se s'executaran de forma paral·lela a la lògica de la interfície gràfica, utilitzant fils d'execució.
IMPORTANT: Cal tenir en compte les limitacions de processament dels dispositius mòbils a l'hora de programar fils d'execució en Java ME. La creació de fils d'execució és una tasca costosa i per tant, hem d'intentar crear només els fils d'execució imprescindibles.
El cas més habitual d'ús de fils d'execució en Java ME esdevé al utilitzar l'API GCF. Molts MIDlets tard o d'hora requereixen connectar-se a la xarxa. Com que actualment les connexions a la xarxa són lentes, és necessari utilitzar fils d'execució per tal de no bloquejar l'execució de la lògica de la interfície gràfica. A més, cal proporcionar una interfície gràfica adequada a l'usuari que li mostri que el mòbil està executant un procés.
L'apartat 4.2 del projecte Gestió Aules, mostra un exemple pas a pas de com utilitzar fils d'execució per establir una connexió HTTP.
Recursos:
Recursos:
Llibre sobre MMAPI
MMAPI ha estat dissenyat per a suportar diversos continguts multimèdia i proporcionar mecanismes de captura per a àudio i vídeo. MMAPI és l'especificació de Java JSR 135. La implementació proporcionada per Sun proporciona suport bàsic per a la reproducció simple d'àudio.
En molts sentits MMAPI s'assembla a l'API Generic Connection Framework (GCF).
No tots els dispositius suporten MMAPI. Podeu consultar la llista a:
http://developers.sun.com/mobility/device
Característiques:
Recursos:
Els paquets:
Són els que contenen MMAPI.
MMAPI està centrat en la classe Manager. Manager és una factoria que permet crear objectes Player. Per tal d'indicar l'origen d'un medi multimedia s'utilitzen URI o objectes DataSource. Un objecte Player especifica les seves característiques funcionals (i opcionals) com control de volum, capactitat de reproducció/grabació, etc. mitjançant les interfícies Control.
Per exemple, un reproductor de tons disposarà de la classe ToneControl, un capturador de vídeo de VideoControl i RecordControl.
La següent gràfica mostra l'arquitectura de l'API MMAPI:
La classe Manager és un classe factoria estàtica encarregada de crear objectes MMAPI. Per exemple, el mètode createPlayer() s'encarrega de crear objectes Player.
static Player createPlayer(String locator)
Hi ha 3 tipus de locator:
Un locator és similar a una URI i permet executar recursos remots. Per exemple, es pot crear un player per a un wav remot amb:
createPlayer("http://www.yourwebsite.com/audio/song.wav")
Una sèrie d'exemples:
Tipus de medi | Exemple |
---|---|
Captura d'àudio | "capture://audio" captura àudio del dispositiu de captura d'àudio per defecte. |
Captura d'audio | "capture://devmic0?encoding=pcm" captura d'àudio del dispositiu devmic0 i codificació PCM |
Captura de vídeo | "capture://video" captura vídeo del dispositiu de captura de vídeo per defecte. |
Captura de vídeo | "capture://devcam0?encoding=rgb888&width=100&height=50" captura vídeo del dispositiu de captura de vídeo devcam0 amb unes mides específiques. |
Escoltar la radio | "capture://radio?f=105.1&st=stereo" en estèreo i al 105FM |
rtp media | "rtp://host:port/type" |
Executar un to | "device://tone" proporciona un reproductor per a executar tons |
Executar un midi | "device://midi" proporciona un reproductor per a executar un MIDI |
No tots els protocols seran suportats per tots els dispositius. El mètode:
static String[] getSupportedProtocols(String contentType)
El tipus de dades de cada URI dependrà del MIME Type. Per exemple la següent URI:
http://host/sample.mid
Serà associada per un servidor web HTTP al MIME-Type audio/midi i per tant el mòbil crearà un reproductor de fitxers MIDI. Els MIME-type que suporta cada dispositiu varien segons el fabricant i el model del mòbil.
Per a Nokia podeu obtenir una llista a
http://sw.nokia.com/id/444aa596-38d4-473c-81ed-657da920d163/Browser_MIME_Types_in_Nokia_GSM_Devices_v1_8_en.pdf
En temps d'execució podeu obtenir una llista dels MIME-type suportats amb el mètode:
static String[] getSupportedContentTypes(String protocol)
Finalment també disposem del mètode:
static Player createPlayer(InputStream stream, String type)
Que permet crear un player a partir d'un stream de dades qualsevol. La variable type serveix per especificar el MIME-type.
Per exemple podem llegir directament de la base de dades RMS:
... RecordStore store; int id; // Reproduir des de un RecordStore try { InputStream is = new ByteArrayInputStream(store.getRecord(id)); Player player = Manager.createPlayer(is, "audio/X-wav"); p.start(); } catch (IOException ioe) { } catch (MediaException me) { } ...
La interfície Player conté les funcionalitats comunes a tots els tipus de reproductor multimèdia. Un reproductor té un cicle de vida format per una sèrie d'estats:
L'estat d'un reproductor té un gran impacte sobre el consum de recursos (per exemple un capturador de vídeo que no s'hagi tancat no permetrà a altres aplicacions utilitzar la càmera).
Estats
Altres mètodes de la interfície Player:
Els següents mètodes estan disponibles a tots els Players:
String getContentType ()
long getDuration ()
long getMediaTime ()
long setMediaTime (long now)
int getState ()
TimeBase getTimeBase ()
void setTimeBase (TimeBase master)
void setLoopCount (int count)
NOTA: Tots els temps estan en microsegons.
La interfície Control defineix que un objecte és de tipus Control. Un objecte de Control és utilitzat per a realitzar operacions específiques amb un mitja multimèdia.
La interfície Player implementa la interfície Controllable i per aquesta rao disposa dels mètodes per obtenir els controls d'un reproductor concret:
Control getControl(java.lang.String controlType)
Control[] getControls()
Múltiples controls poden ser implementats en un mateix objecte.
L'API base de Java ME només ofereix 2 controls:
Amb MMAPI 1.2 disposem de més controls:
FramePositioningControl | GUIControl MetaDataControl | MIDIControl PitchControl | RateControl RecordControl | StopTimeControl TempoControl | ToneControl VideoControl | VolumeControl
Un exemple d'ús de controls:
Player p; VideoControl vc; try { p = Manager.createPlayer("http://server/somemovie.mpg"); p.realize(); // Obtenir el control de volum vc = (VideoControl) p.getControl("VideoControl"); .... p.start(); } catch(IOException ioe) { } catch(MediaException me) { }
Permet controlar els esdeveniments d'un reproductor. La interfície PlayerListener declara només un mètode:
void playerUpdate(Player player, String event, Object eventData)
Aquest mètode és invocat cada cop que succeïx un esdeveniment de reproductor.
Els tipus d'esdeveniments són declarats com a variable estàtiques a la interfície PlayerListener:
Un parell de coses ha tenir en compte:
Hi han dos mètodes a la classe Player que permeten afegir/treure control d'esdeveniments:
void addPlayerListener (PlayerListener listener) void removePlayerListener (PlayerListener listener)
Recursos
La classe Manager proporciona entre d'altres mètodes un mètode per executar sons simples basats en tons. Aquest mètode és Manager.playTone().
Un mètode simple com:
private void simpleTone(int note, int duration) { try { Manager.playTone(note, duration, 80 /*vol*/); } catch (Exception ex){ System.out.println(ex); } }
El podem utilitzar per a produir diferents tipus de tons. Dels exemples (Samples de Netbeans) us mostrem uns quants exemples:
Un to simple i curt:
simpleTone(ToneControl.C4, 100);
Un to simple llarg
simpleTone(ToneControl.C4 + 4, 1000);
On ToneControl és una interfície que permet controlar els tons.
ToneControl.C4 és un Do (C en anglès). De fet és el Do central d'un teclat i el seu valor és 60.
Els possibles tons van de 0 (C-1) a 127 (G9) on C-D-E-F-G-A-B és una octava de l'escala musical :
tone = 12 * log2 (freq/220) + 57
Note |
Frequency (Hz) |
MIDI Tone |
A4 |
440.00 |
69 |
A# |
466.16 |
70 |
B |
493.88 |
71 |
C |
523.25 |
72 |
C# |
554.36 |
73 |
D |
587.33 |
74 |
D# |
622.25 |
75 |
E |
659.25 |
76 |
F |
698.45 |
77 |
F# |
739.98 |
78 |
G |
783.99 |
79 |
G# |
830.60 |
80 |
Recursos:
try { Player p = Manager.createPlayer ("http://server/somemusic.mp3"); p.setLoopCount(5); p.start(); } catch(IOException ioe) { } catch(MediaException e) { }
Amb control de volum:
Player p; VolumeControl vc; try { p = Manager.createPlayer("http://server/somemusic.mp3"); p.realize(); // Obtenir el control de volum i establir el volum al màxim? vc = (VolumeControl) p.getControl("VolumeControl"); if(vc != null) { vc.setVolume(100); } p.prefetch(); p.start(); } catch(IOException ioe) { } catch(MediaException e) { } ...
try { Player p = Manager.createPlayer( "http://www.fileformat.info/format/mpeg/sample/567fd6a0e0da4a8e81bdeb870de3b19c/DELTA.MPG"); p.realize(); GUIControl gc; if ((gc = (GUIControl) p.getControl("GUIControl")) != null) { Form form = new Form("My GUI"); form.append((Item) gc.initDisplayMode(GUIControl.USE_GUI_PRIMITIVE, null)); Display.getDisplay(this).setCurrent(form); } p.start(); } catch (MediaException pe) { } catch (IOException ioe) { }
new Thread() { public void run() { byte[] melodia = { ToneControl.VERSION, 1, ToneControl.TEMPO, 22, ToneControl.BLOCK_START, 0, 60, 8, 62, 4, 64, 4, 65, 4, 67, 4, 69, 4, 71, 4, 72, 4, 74, 4, 76, 4, 77, 4, 79, 4, 81, 4, 83, 4, 84, 4, 83, 4, 81, 4, 80, 4, 81, 4, 86, 4, 84, 4, 83, 4, 81, 4, 81, 4, 79, 4, 78, 4, 79, 4, 60, 4, 79, 4, 88, 4, 79, 4, 57, 4, 77, 4, 88, 4, 77, 4, 59, 4, 77, 4, 86, 4, 77, 4, 56, 4, 76, 4, 86, 4, 76, 4, 57, 4, 76, 4, 84, 4, 76, 4, 53, 4, 74, 4, 84, 4, 74, 4, 55, 4, 74, 4, 83, 4, 74, 4, 84, 16, ToneControl.SILENCE, 16, ToneControl.BLOCK_END, 0, ToneControl.BLOCK_START, 1, 79, 4, 84, 4, 88, 4, 86, 4, 84, 4, 83, 4, 81, 4, 79, 4, 77, 4, 76, 4, 74, 4, 72, 4, 71, 4, 69, 4, 67, 4, 65, 4, 64, 8, 76, 8, 77, 8, 78, 8, 79, 12, 76, 4, 74, 8, ToneControl.SILENCE, 8, ToneControl.BLOCK_END, 1, ToneControl.SET_VOLUME, 100, ToneControl.PLAY_BLOCK, 0, ToneControl.SET_VOLUME, 50, ToneControl.PLAY_BLOCK, 0, ToneControl.SET_VOLUME, 100, ToneControl.PLAY_BLOCK, 1, ToneControl.SET_VOLUME, 50, ToneControl.PLAY_BLOCK, 1, ToneControl.SET_VOLUME, 100, ToneControl.PLAY_BLOCK, 0,}; try { Player player = Manager.createPlayer(Manager.TONE_DEVICE_LOCATOR); player.realize(); player.setLoopCount( -1); ToneControl control = (ToneControl) player.getControl("javax.microedition.media.control.ToneControl"); control.setSequence(melodia); player.start(); } catch (IOException ex) { ex.printStackTrace(); } catch (MediaException ex) { ex.printStackTrace(); } } }.start();
Per executar un player en un nou fil d'execució:
new Thread(new Runnable() { public void run() { //Poseu aquí el codi del player } }).start();
Per exemple:
new Thread(new Runnable() { public void run() { Manager.PlayTone(ToneControl.C4, 100,100); } }).start();
Recursos:
Exemples d'aplicacions: Google Lattitude.
Android té un API.
Recursos:
JSR 82 és la API que permet als desenvolupadors, crear aplicacions que utilitzin la tecnologia Bluetooth. L'API també es anomenada Java platform access Bluetooth through the Java APIs for Bluetooth Wireless Technology (JABWT o BTAPI)
Bluetooth pot ser utilitzar per intercanviar fitxers, fotos, business cards o qualsevol tipus de dades entre dos dispositius mòbils.
MSA: Mobile Service Architecture http://www.google.es/url?sa=t&source=web&ct=res&cd=1&url=http%3A%2F%2Fjava.sun.com%2Fjavame%2Freference%2Fdocs%2Fmsa_datasheet.pdf&ei=LruRSaqoPJKT_gbDvrHBDA&usg=AFQjCNGq6NmKcq2Fp31FmoQkvuAQFbpwVQ&sig2=zhZMU5-WshwOfJIv515f-w
També anomenada
Obex:
OBEX és l'abreviatura de OBject EXchange, és un protocol de comunicacions que facilita l'intercanvi de fitxers binaris entre dispositius. El protocol es mantingut per la Infrared Data Association i ha estat adoptat per Bluetooth. Inicialment s'utilitzava amb sistemes d'infrarojos.
--acacha 18:51, 10 febr 2009 (CET)
Wireless Toolkit:
Suport la API i el test d'aplicacions Bluetooth sense disposar de maquinari bluetooth.
Recursos:
L'API per a serveis web (també coneguda per les inicials WSA - web services api-) està dissenyada per tal de funcionar amb CDC i CLDC. Ha estat desenvolupada per la comunitat Java (JSR 172) i és una API opcional de Java ME.
Característiques:
L'API té 2 parts:
Serveis web
Segons el W3C, un Servei Web és un programari dissenyat per ser interoperable entre màquines a través d'una xarxa (P. ex. Internet).
Un servei web ha de tenir una interfície que sigui descrita en un format processable per màquines (p. ex. WSDL). Els sistemes interactuen amb els SW segons la seva interfície utilitzant missatges (SOAP Envelopes) o segons la filosofia REST.
L'objectiu que persegueixen els serveis web és la interoperabilitat (un client PHP, Python, corrent en una plataforma Windows utilitzant un servei web Java en plataforma Linux... ) i per aquesta raó esta pensat per utilitzar estàndards madurs com XML i HTTP.
Els principals responsables de l'arquitectura i la estandardització dels serveis web són:
En mòbils l'arquitectura d'un servei web és la següent:
Analitzadors sintàctics (parsers):
El verb to parse en anglès significa analitzar sintàcticament.
En informàtica, "parsejar" és el procés d'analitzar una seqüència d'entrada per tal de determinar la seva estructura gramatical respecte a una gramàtica formal específica (en el nostre cas XML).
Un analitzador sintàctic o "parser" és un programa informàtic encarregat d'aquesta tasca. Amb Java analitzar sintàcticament documents XML representarà transformar documents XML a una estructura d'arbre o classes Java.
L'anàlisi sintàctic té 2 fases:
L'analogia és clara amb l'anàlisi sintàctic en llengua. En XML els nodes representen els tokens i l'arbre sintàctic és l'estructura XPath d'un document XML.
Un document XML té una estructura en arbre. Podem distingir 2 tipus principals d'API a l'hora de processar document XML:
Java ME només suporta SAX. Més adequada a les restriccions de memòria dels dispositius mòbils
SAX vs DOM:
SAX:
SAX (Simple API for XML (SAX)):
Es tracta d'un estàndard “de facto” per parsers Java basats en processament per esdeveniments. Java ME treballa amb la versió 2.0. SAX va ser desenvolupat originalment de forma col·laborativa pel grup de treball XML-DEV. Podem trobar altres implementacions en altres llenguatges (Python, Perl, etc.).
SAX funciona per esdeveniments:
Amb Java ME utilitzarem JAXP (Java API for XML processment). Aquesta API proveeix de capacitat a Java per analitzar sintàcticament, validar i transformar documents XML.
Java ME no suporta tot JAXP:
Els paquets de la API són:
javax.xml.parsers org.xml.sax org.xml.sax.helpers
Anem a veure un exemple de com analitzar sintàcticament un document XML amb SAX. Utilitzant en patró de disseny de factoria, podem obtenir un nou parser:
SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser saxParser = factory.newSAXParser();
El mètode més important de la classe SAXParser és parse(). Tenim 2 opcions:
parse(InputSource is, DefaultHandler dh) parse(java.io.InputStream is, DefaultHandler dh)
Com podeu veure teniu 2 paràmetres que indiquen:
Imagineu-vos que voleu analitzar el següent document XML:
<grup nom="1r ESI Tarda"> <alumne> <nom>Pere</nom> <cognoms>Abad Abellà</cognoms> <foto></foto> </alumne> <alumne> <nom>Pau</nom> <cognoms>Ferret Amela</cognoms> <foto>
</foto> </alumne> <alumne> <nom>Joan</nom> <cognoms>Coll Català</cognoms> <foto>
</foto> </alumne> <alumne> <nom>Julia</nom> <cognoms>Mauri Soler</cognoms> <foto>
</foto> </alumne> </grup>
Un possible Handler podria ser el següent:
public class GrupHandler extends DefaultHandler { ... public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if ("grup".equals(qName)) { stack.push(new Grup()); String nom= attributes.getValue("nom"); Grup grup = ((Grup) stack.peek()); grup.setNom(nom); } else if ("alumne".equals(qName)) { stack.push(new Alumne()); } else if ("nom".equals(qName) || "cognoms".equals(qName) || "foto".equals(qName)) { stack.push(new StringBuffer()); isStackReadyForText = true; } } public void characters(char[] ch, int start, int length) throws SAXException { // if stack is not ready, data is not content of recognized element if (isStackReadyForText == true) { ((StringBuffer) stack.peek()).append(ch, start, length); } else { // read data which is not part of recognized element } } public void endElement(String uri, String localName, String qName) throws SAXException { StackReadyForText = false; Object tmp = stack.pop(); if (qName.equals("grup")) { grup = (Grup) tmp; } else if (qName.equals("alumne")) { ((Grup) stack.peek()).getAlumnes().addElement((Alumne) tmp); } else if (qName.equals("nom")) { ((Alumne) stack.peek()).setNom( tmp.toString() ); } else if (qName.equals("cognoms")) { ((Alumne) stack.peek()).setCognoms(tmp.toString() ); } else if (qName.equals("foto")) { ((Alumne) stack.peek()).setFotoURL(tmp.toString() ); } else { stack.push(tmp); } }
Com podeu veure s'utilitza una PILA per tal d'accedir a la informació d'esdeveniments anteriors. En aquest exemple estem convertint el fitxer XML en un (o uns) objectes Java que emmagatzemen la informació del document XML. Es tracta d'una forma de persistència. A aquest procés se l'anomena marshalling/unmarshalling.
La pila té 2 operacions:
Funcions startElement
Funcions EndElement:
Funció Characters
S'omple l'objecte amb el valor del text del node simple
Hi han llibreries com Commons Digester que estan especialitzades en el procés de marshalling/unmarshalling.
Recursos:
L'API opcional Wireless Messaging API (WMA) proporciona a Java ME la possibilitat d'utilitzar amb independència de la plataforma serveis de missatgeria com SMS. WMA pot ser utilitzat amb CLDC i MIDP.
WMA és una api molt relacionada amb l'API GCF:
Recursos:
JMUnit és un framework de proves unitàries (unit test framework) basat en JUnit però adaptat per a Java ME.
En programació, els testos unitaris és un mètode de disseny i desenvolupament d'aplicacions mitjançant el qual el programador verifica que unitats individuals de codi funcionen correctament. Una unitat (unit) és la part més petita que pot ser provada d'una aplicació.
En programació procedimental, una unitat pot ser un program, una funció, un procediment, etc. En programació orientada a objectes, la unitat més petita de test és el mètode.
Unit testing millora la qualitat del codi provant de forma local els components individuals d'una aplicació. És una eina útil per a identificar i solucionar errors de forma ràpida.
Junit és una eina per als test unitaris molt utilitzada en la versió estàndard de Java (Java SE) i en la versió Enterprise (Java EE). El problema però, és que Junit es basa en l'API de reflexió i com ja hem vist anteriorment aquesta API no esta completament suportada a Java ME. Per als desenvolupadors de Java ME hi ha dos alternatives:
Tant Netbeans (des de la versió 5.5) com Eclipse, portant incorporat suport per a les proves unitàries amb JMunit.
Propietats:
Alternativa:
Recursos:
De forma similar al que succeïx amb Java SE, amb Java ME dins del paquet java.lang trobem la classe System. Entre d'altres coses, la classe System permet accedir a les propietats del sistema: Per exemple, els següent codi permet accedir a la propietat de sistema microedition.locale :
String locale = System.getProperty("microedition.locale");
Les propietats són valors de configuració formats per una parella clau/valor. La clau s'utilitza per identificar el valor i tant clau com valor són de tipus text. Les propietats del sistema són un subconjunt de propietats que proporcionen informació sobre el sistema on s'està executant una aplicació per a dispositius mòbils.
Algunes de les propietats de sistema més habituals en Java són:
Amb Java SE hi ha un suport molt més potent per a treballar amb propietats (per exemple la classe java.util.Propierties). Tota aquesta funcionalitat no està disponible en Java ME.
També cal que tingueu en compte que en entorns mòbils Java ME, hi ha propietats de sistema que són específiques d'aquest entorn. Fins i tot, la existència o no de certes variables del sistema dependrà de la implementació de Java ME (p. ex. pot ser diferent en un Nokia, que en un Siemens, i fins i tot diferent entre diferents models d'una marca concreta).
Alguns exemples habituals de variables de sistema amb Java ME:
A l'adreça de la web de nokia:
http://www.forum.nokia.com/info/sw.nokia.com/id/37086440-dcce-4fb4-aa3e-8d8c16d62b33/MIDP_System_Properties_v1_2_en.zip.html
Podeu trobar un document amb més exemples de propietats de sistema Java ME i un MIDLet que us permet consultar les propietats de sistema del vostre mòbil.
Recursos:
La internacionalització és el procés de dissenyar programari que pugui ser adaptat a múltiples llengües i regions del món sense la necessitat d'aplicar canvis en el codi. En canvi, la localització és el procés d'adaptar programari per a una zona i/o llengua específica.
Sovint s'utilitzen els termes i18n i i10n per tal de referir-se a internacionalització i localització respectivament. Aquests termes tenen el seu origen en agafar la primera i la última lletra dels vocables internacionalization i localization i comptar el nombre de lletres que hi ha entre mig d'aquestes lletres (18 en el cas d'internacionalització i 10 en el cas de localització).
I com es duu a terme la internacionalització d'aplicacions? El sistema més utilitzat és guardar els textos susceptibles de ser traduïts en fitxers de text independents del codi font. D'aquesta forma es poden modificar els textos sense necessitat de tornar a compilar i es poden afegir tants fitxers de text, com idiomes es vulguin suportar.
En Java utilitzarem fitxers de propietats (també anomenats Resource Bundles). Resource Bundles es pot traduir com a paquets de recursos. En Java és comú anomenar als fitxers d'internacionalització:
message.properties
Aquest fitxer conté l'idioma per defecte. Si tenim altres idiomes, els noms del fitxers serien:
messages_es_MX.properties messages_zh_CN.properties messages_cs_CZ.properties messages_ja_JP.properties messages_de.properties messages.properties
Però com llegim aquests fitxers d'internacionalització i els utilitzem en el nostre codi font? Amb Java SE tenim classes de suport com java.util.ResourceBundle i d'altres que faciliten la tasca de crear aplicacions internacionals. A més, existeixen llibreries extres de suport a i18n, com les proporcionades per jakarta-commons de la Apache Software Foundation.
IMPORTANT: A Java ME NO tenim cap mena de suport. S'ha d'implementar des de zero
Per sort, el WTK proporciona un exemple d'una classe que ens permet gestionar la internacionalització. L'exemple el podeu trobar a:
~/netbeans-6.5/mobility8/WTK2.5.2/apps/i18nDemo
La classe s'anomena LocalizationSupport. La majoria de IDEs (com Netbeans o Eclipse) permeten treballar amb aquests tipus de fitxers. Per exemple, amb Netbeans podeu crear un fitxer d'internacionalització amb l'assistent de creació de fitxers (Ctrl+n o Menú File>New):
Per obtenir un missatge:
LocalizationSupport.getMessage("CLAU_DEL_MISSATGE")
Recursos:
Consulteu l'article WAP.
Idees d'aplicacions Java:
Podeu trobar la documentació de referència de Java ME a:
http://java.sun.com/javame/reference/apis.jsp
Amb Netbeans, tenim la documentació al disc dur local. La podeu trobar a:
~/netbeans-6.5/mobility8/WTK2.5.2/docs/api
$ cd ~/netbeans-6.5/mobility8/WTK2.5.2/docs/api $ ls jsr082 jsr135 jsr172 jsr177 jsr211 jsr239 midp
Si entreu a la carpeta midp, podeu mostrar la API executant:
$ cd midp/ $ ls allclasses-frame.html constant-values.html help-doc.html index.html javax overview-summary.html package-list stylesheet.css allclasses-noframe.html deprecated-list.html index-all.html java overview-frame.html overview-tree.html resources $ firefox index.html
Aquesta és la documentació de MIDP 2.0 (JSR 118) del CLDC
Tota la resta de carpetes corresponen als paquets opcionals:
Si us trobeu que la vostra aplicació Java ME es penja durant l'intent de connexió (pantalla airtime):
Aleshores és que no esteu utilitzant els fils d'execució correctament. Consulteu l'apartat Threading d'aquest article.
També us pot ser de molta ajuda el següent enllaç: