1 Seminar zum Programmierprojekt SS 2008 Uni Tübingen, Arbeitsbereich Technische Informatik Ausgabe: 20. Mai 2008 Anleitung B5 Einführung in die MIDlet-Programmierung mit Java2ME Allgemeines Mit Aufgabe 5 beginnen Sie die Implementierungsphase des Praktikums. Auf den Praktikumsrechnern 2 und 3 ist Java2ME (Wireless Toolkit) und eclipse installiert. Ziel diese Aufgabe ist es, ein MIDlet zu Programmieren, welches auf Benutzereingaben reagieren kann, eine Inquiry durchführt, die Dienstabfrage an gefundene Geräte schickt und die daraufhin erhaltenen Dienste dem Nutzer präsentiert. Das Nutzen der Dienste sowohl der Host als auch der BTnodes soll in den Aufgaben B6 und B7 implementiert werden. Java2ME Einführung J2ME ist eine Umsetzung der Programmiersprache Java für "embedded consumer products" wie etwa Mobiltelefone oder PDAs. JSR 30 und JSR 37 (Java Specification Request) definieren J2ME. Die Java Micro Edition kann in verschiedenen Konfigurationen eingesetzt werden. Eine Konfiguration ist eine Menge von Java APIs, Klassenbibliotheken und einer virtuellen Maschine, die auf eine bestimmte Gerätefamilie mit ähnlichen Anforderungen konzipiert ist, d.h. eine Konfiguration stellt eine Spezifikation für eine Familie von Endgeräten dar. Die Entwicklung von Konfigurationen für J2ME wird hauptsächlich durch die Firma Sun und deren Entwicklungspartnern vorangetrieben. Derzeit existieren zwei Standardkonfigurationen, nämlich Connected Device Configuration (CDC) und Connected Limited Device Configuration (CLDC). Die CDC (Connected Device Configuration, JSR 218) ist eine J2ME Konfiguration, die für Geräte bestimmt ist, die eine komplette Implementierung der JVM und eine Menge von APIs benötigen, die durch das Hinzufügen von Profilen die gesamte Java SE Plattform API einschließen können. Typische Implementierungen benutzen nur einen Teil der SE APIs, indem nur eine bestimmte Anzahl an Profilen eingebunden wird. Die ganze Spezifikation wird in JSR 218 beschrieben. Die CLDC (Connected Limited Device Configuration, JSR 30 und JSR 139) definiert die kleinstmögliche Konfiguration einer J2ME-Laufzeitumgebung. Zu den größten Einschränkungen in Version 1.0 zählt der Verzicht auf Fließkommaberechnungen. Das betrifft sowohl Variablen, die als float, double, als auch als java.lang.Float und java.lang.Double deklariert sind. Weiterentwicklungen der CLDC in Version 1.1 führten u. a. zu einer Wiedereinführung der Fließkommaunterstützung. Insbesondere Spielentwickler sind gelegentlich auf Fließkommaberechnungen angewiesen - wenngleich aus Performance-Erwägungen in vielen Fällen doch eher Festkommazahlen bevorzugt werden. 2 CLDC wird hauptsächlich in Mobilen Geräten in Verbindung mit dem "Mobile Information Device Profile" (MIDP) benutzt. MIDP (Mobile Information Device Profile) ist ein Profil der Java 2, Micro Edition (J2ME), das speziell auf die Fähigkeiten kleiner mobiler Endgeräte wie Mobiltelefon oder Pager ausgelegt ist. Profile sind die APIs, die es für eine Konfiguration gibt. Sie stellen eine weitere Art der Anpassungsmöglichkeit der Java2 API an Anforderungen für Gerätefamilien dar. Applikationen auf Basis von MIDP nennt man kurz MIDlets (z.B. Java-Spiele für Handys). Das MIDP umfasst Funktionen zur Ansteuerung und Abfrage von Einhandtastaturen, Miniaturbildschirmen, flüchtigen und nicht-flüchtigen Speichern im Kilobyte-Bereich etc. Es existieren bisher das MIDP 1.0 (JSR 37) und das MIDP 2.0 (JSR 118), das einige Erweiterungen aufweist. MIDP 3.0 ist bereits als JSR 271 in Bearbeitung. Konfigurationen bieten eine breite Variabilität für verschiedene Einsatzzwecke, sind jedoch oft nicht ausreichend, um die komplette Funktionalität verschiedener Geräte verwenden zu können. Daher gibt es Erweiterungen, die interne Gerätefunktionen unterstützen, wie z.B. Bluetoothfunktionalität (siehe JSR 82), die für unsere Zwecke benutzt werden wird. Abbildung 1 zeigt die in JSR 82 spezifizierte CLDC/MIDP-Architektur. Die Entwicklung von Konfigurationen, Profilen und Erweiterungen wird im Rahmen des "Java Community Process" (JCP) durchgeführt, an denen auch namhafte Firmen wie Nokia, Siemens oder IBM beteiligt sind. Abbildung 1: CLDC/MIDP-Architektur, spezifiziert in JSR082 Im Praktikum verwenden wir das Mobiltelefon Nokia 6680, das MIDP 2.0 unterstützt. Eine Übersicht über das Nokia 6680 finden Sie hier: http://www.nokia.de/de/mobiltelefone/modelluebersicht/6680/startseite/150206.html Folgende Konfigurationen und Erweiterungen werden vom Nokia 6680 unterstützt: o Connection Limited Device Configuration 1.0 (CLDC 1.0) o Connection Limited Device Configuration 1.1 (CLDC 1.1) 3 o Mobile Information Device Profile MIDP 1.0 und MIDP 2.0 o Java Technology for the Wireless Industry (JTWI), JSR 185 o Mobile Media API V1.0 (MMAPI) JSR 135 o Wireless Messaging API V1.0 (WMAPI) JSR 120 o Bluetooth API (BTAPI) JSR 82 Als Benutzeroberfläche bieten MIDP-APIs eine Menge von User-Interface-Elementen (UI). Diese ermöglichen Interaktionen zwischen Benutzer und MIDlet und befinden sich im Paket javax.microedition.lcdui. Abbildung 2: High level und Low level MIDP API Die High-Level-API (vgl. Abb.2)stellt Ein- und Ausgabefelder, wie z. B. Textfelder (TextField) oder Fortschrittsanzeigen (Gauge), zur Verfügung. Sie sind der Elternklasse Item untergeordnet. Objekte von "Item" können auf einem Formular platziert werden, sind jedoch nur eingeschränkt positionierbar. Formulare sind Objekte der Klasse "Form". Sie können an das aktuelle Display angehängt werden und verschiedene UI-Elemente beinhalten. Das MIDlet kann Wechsel zwischen Formularen anfordern sowie während der Laufzeit UI-Elemente hinzufügen und auf Benutzereingaben reagieren. Die wichtigsten UI-Elemente sind(vgl. Abb. 2): • Form: Container für andere UI-Elemente • Item: Repräsentiert einen Menüeintrag. Mehrere Items können in einem Menü zusammengefasst und an ein Formular angehängt werden • Alert: Popup-Nachrichten die den Benutzer über Fehler, Exceptions, Warnungen oder über sonstige Informationen benachrichtigen 4 • ChoiceGroup: Implementiert eine Selektionsmöglichkeit zwischen mehreren Einträgen. Die Auswahl ausschließlich einzelner (engl. "single choice") oder auch mehrerer Einträge (engl. "multiple choice") ist möglich • TextBox: Einzeilige Eingabefelder, in denen der Benutzer Text einfügen bzw. editieren kann • TextField: Ähnlich einer TextBox, allerdings mehrzeilig • Gauge: Fortschrittsanzeige • Ticker: Anzeige von bewegtem Text Im Gegensatz hierzu arbeitet die Low-Level-API auf Pixelebene. Die Klasse "Canvas" ist der Eingangspunkt für graphische Zeichnungen. Sie selbst beinhaltet hierfür keine Methoden, jedoch stellt sie die Callback-Funktion paint() bereit. Sie wird immer dann aufgerufen, wenn der Programmmanager entscheidet, das Display neu zu zeichnen. Ihr einziger Parameter ist ein Objekt Graphics, welches sämtliche Zeichnungsfunktionen, wie beispielsweise drawLine() zum Zeichnen einer Linie oder fillRect() zum Ausfüllen eines Rechtecks, beinhaltet. Grundsätzlich kann man zwischen reinen Hintergrundapplikationen und jenen unterscheiden, die mit dem Benutzer interagieren. Interaktive Applikationen können auf das Display über ein Objekt Display zugreifen. Man erhält es als Rückgabeobjekt der statischen Methode getDisplay() mit dem MIDlet als Argument. Die Methode setCurrent() bestimmt, welches Objekt Displayable den Inhalt eines Displays darstellen soll. Displayable ist die Elternklasse von Screen und Canvas. Ihr sind alle UI-Klassen unterstellt. Mit anderen Worten, sie definiert sämtliche Objekte die am Display angezeigt werden können. 5 MIDLET Programmierung auf dem Mobiltelefon In Aufgabe B4 haben Sie bereits gesehen, welche Funktionalität ein MIDlet bietet. Die Bluetooth Demo aus dem Wireless Toolkit sollte Ihnen dabei zeigen welche einzelnen Schritte für eine Bluetooth Kommunikation nötig sind. Nun ist es Ihre Aufgabe ein eigenes MIDlet zu entwerfen, welches den im Pflichtenheft definierten Anforderungen entspricht. Dabei soll in dieser Aufgabe das Augenmerk auf dem Starten des MIDlets, dem Suchen nach Bluetooth-Geräten in der Umgebung und der Dienstanfrage liegen. Dazu gibt es eine gute Einführung in einer Masters-Thesis von André N. Klingsheim, siehe [6]. Darin wird der Aufbau eines MIDlets Schritt für Schritt erklärt und mit beigefügtem Source-Code erläutert. Allerdings benutzt Klingsheim die „Service-Discovery“Methode, die in unserer Umgebung mit dem Nokia 6680-Telefon nicht funktioniert. (Wir verwenden dafür das Serial Port Profile SPP). Implementieren Sie das dort vorgestellte MIDlet und fügen Sie folgende Funktionalität hinzu: o In der Funktion startApp() soll dem Benutzer ein Willkommensbildschirm präsentiert werden, welcher die Möglichkeit bietet mit der DeviceSearch zu beginnen, oder das Programm zu beenden o In der Funktion commandAction(Command c, Displayable s) werden die Benutzereingaben verarbeitet. So soll z.B. bei Command „OK“ auf Menüeintrag „Start DeviceSearch“, welchen Sie aus dem Objekt Displayable s herauslesen können, die Inquiry gestartet werden, bzw. wie im Beispiel eine Funktion doDeviceDiscovery() ausgeführt werden. o In der Funktion inquiryCompleted(int param) soll dem Benutzer die Liste der gefundenen Geräte präsentiert werden. Dabei müssen Sie darauf achten, dass die Liste der Geräte auf dem Display auswählbar ist, sodass es möglich ist, ein Gerät zu selektieren und die Service-Suche zu starten. o Da die Service-Suche über das DiscoveryListener Interface aufgrund von Kompatibiltätsproblemen nicht einwandfrei funktioniert, können Sie die Funktionen o public void servicesDiscovered(int transID, ServiceRecord[] serviceRecord){} und o public void serviceSearchCompleted(int transID, int respCode){} leer lassen, müssen sie jedoch aufgrund des Interfaces so übernehmen. o In der Funktion doDeviceDisocovery() soll dem Nutzer an allen Stellen an denen // Error handling Code here steht eine Fehlermeldung ausgegeben werden, aus welcher ersichtlich ist, an welcher Stelle das Programm einen Fehler ausgeworfen hat. o Zur Dienstsuche müssen Sie in der Funktion commandAction(Command c, Displayable s) bei Command „OK“ und einem ausgewählten Gerät den in der Spezifikation definierten String an das betreffende Gerät senden. Dienstsuche Zur Dienstsuche können Sie folgendes Verfahren benutzen: Zunächst benötigen Sie die Bluetooth-Adresse des betreffenden Geräts. Diese erhalten Sie aus dem Objekt RemoteDevice in der Funktion deviceDiscovered(). Sie benötigen dann eine Verbindung zu diesem RemoteDevice: 6 StreamConnection streamConn = (StreamConnection) Connector.open(url); Die Connector-Klasse aus javax.microedition.io.Connector öffnet eine Verbindung und gibt ein Connection-Objekt zurück, welches in die entsprechende Verbindungsart gecastet werden kann (hier also StreamConnection). Der url-Parameterstring muss dem URL-Format aus RFC 2396 entsprechen. Er hat die allgemeine Form wie folgt: {scheme}:[{target}][{params}] • {Scheme}: Name des Protkolls, z.B. btspp oder http • {target}: Eine Netzwerkadresse, "bluetooth_address_of_remoteDevice:1 • [{params}]: Weitere Parameter der Form ";x=Y", z.B. ";app-name=MobilerKnoten" Wird das ServiceDiscoveryProtocol verwendet, dann wird hinter der {target}-Netzwerkadresse die PSM (Protocol Service Muliplexer, wichtigster Parameter der L2CAPVerbindung, siehe [14]) angegeben, um einen speziellen Dienst im Server anzusprechen. PSM 1 (genauer 0x0001) enspricht z.B. dem ServiceDiscoveryProtocol. Da wir in unserer Umgebung das ServiceDiscoveryProtocol nicht verwenden (können), benötigen wir auch den PSM nicht. Wir verwenden, abhängig vom anzusprechenden Gerät, btl2cap und btspp als {Scheme}.Die Connectorstrings sehen dann folgendermaßen aus: BTNode: „btl2cap://" + [MAC-Adresse des BTN] +":0xE001” …und für den Host: "btspp://" + [MAC-Adresse des Hosts] + ":1;encrypt=false;authenticate=false" Der Verbindungsaufbau zur BTNode L2CAPConnection l2cap; l2cap = (L2CAPConnection) Connector.open(String BTN-ConnectorString (siehe oben)); DataInputStream in = con.openDataInputStream(); DataOutputStream out = con.openDataOutputStream(); Um eine Nachricht zu verschicken sieht die Sache jedoch etwas anders aus: byte[] meineNachricht; l2cap.send(meineNachricht); und um eine Nachricht zu empfangen: byte[] antwort; 7 l2cap.receive(msg); Verbindungsaufbau zum Host: Ausgehenden Stream: DataOutputStream outStream = streamConn.openDataOutputStream(); Senden Sie nun über diese Verbindung den in der Spezifikation festgelegten String. Benutzen Sie für Input und Output über Bluetooth die Funktionen writeUTF(String s) und readUTF() String request = "ihrString"; outStream.writeUTF(request); Da es sich bei der geöffneten Verbindung um eine Verbindung mit Puffer handelt, müssen Sie diesen leeren und somit die Nachricht senden: outStream.flush(); Schließen Sie danach den OutputSream, nicht jedoch ihre Stream Connection: outStream.close(); Danach müssen Sie nur noch auf die Antwort der Gegenstelle warten: Dazu öffnen Sie einen Inputstream und warten auf eine Eingehende Verbindung: DataInputStream inDir = null; inStream = streamConn.openDataInputStream(); String answer=null; answer = inStream.readUTF(); Schließen Sie den InputStream und die StreamConnection inStream.close(); streamConn.close(); Parsen Sie nun den erhaltenen answer String und präsentieren dem Benutzer die angebotenen Dienste. 8 Literatur und URLs [1] http://www.nokia.de/de/mobiltelefone/modelluebersicht/6680/startseite/150206.html [2] http://jcp.org/en/jsr/all [3] http://www.m-software.de/handy-galerie/Nokia/6680.php [4] http://java.sun.com/products/midp/ [5] http://java.sun.com/products/cldc/ [6] http://wireless.klings.org/klings_jabwt_master_thesis.pdf [7] http://de.wikipedia.org/wiki/MIDP [8] http://developers.sun.com/techtopics/mobility/midp/articles/bluetooth2/ [9] http://java.sun.com/products/sjwtoolkit [10] http://www.mobileinfo.com/ [11] http://java.sun.com/javame/reference/apis/jsr082/ [12] http://java.sun.com/javame/reference/apis/jsr139/ [13] http://java.sun.com/javame/reference/apis/jsr118/ [14] Sauter, Martin: Grundkurs Mobile Kommunikationssysteme, Kapitel 5.