Die Ermittlung der Blutalkohol-Konzentration (BAK) ist ein Thema, das schon sehr viele Menschen beschäftigt hat. Schon 1932 hat Widmark eine Formel aufgestellt, die eine Berechnung der maximalen BAK ermöglicht. Da eine Berechnung der BAK aber oft gerade dann gefragt ist, wenn kein Computer in der Nähe ist, ist der mobile Promille-Rechner für das Mobiltelefon eine echte Alternative.
Berechnung der Blutalkohol-Konzentration mit der Widmark-Formel
Die Beziehung zwischen der aufgenommenen Alkoholmenge und der BAK zu einem beliebigen Zeitpunkt ist von den Faktoren Gewicht, Geschlecht und Zeitpunkt der getrunkenen Alkoholmenge abhängig. Diese Beziehung wird in der Widmark-Formel dargestellt.Widmark-Formelc = A / (p * r)c = Alkoholkonzentration in PromilleA = Aufgenommene Alkoholmenge in Grammp = Körpergewicht in Kilogrammr = Reduktionsfaktor (0,7 bei Männern und 0,6 bei Frauen)
AlkoholmengenformelA = m * Vol% * s * rpA = Aufgenommene Alkoholmenge in Grammm = Getrunkene Menge in Liters = Spezifisches Gewicht von Alkohol (0,79)rp = Resorptionsdefizit (0,9)
Im Schnitt baut der Körper 0,1 Promille pro Stunde ab. Da der Abbau nicht sofort beginnt, muss noch eine Karenz von bis zu zwei Stunden berücksichtigt werden. Mit diesen Angaben ist es nun möglich, eine Anwendung zu realisieren, die es ermöglicht, jederzeit die aktuelle Blutalkohol-Konzentration auf Basis der Widmark-Formel zu errechnen.
Manie und Machbarkeit
Diese Berechnungsgrundlage könnte nun als WAP-Applikation realisiert werden und mit dem Handy von unterwegs jederzeit aufgerufen werden. Doch die bekannten Nachteile von WAP, die aus dem Hype einen Flop haben werden lassen, können mit einer J2ME-Anwendung auf einfache Weise gelöst werden. Da zusätzlich zur Oberfläche auch die Logik und die Datenbank auf dem Handy realisiert werden, gibt es keine Verbindungskosten, keine langsame Einwahl oder niedrige Übertragungsgeschwindigkeiten. Alles in allem belaufen sich die Betriebskosten unabhängig von der Nutzungsdauer also auf 0,0 .Von der Idee zum MIDlet
Ein MIDlet besteht aus mindestens zwei Dateien, dem Java Application Descriptor (Jad-Datei) und den Java- Klassen, die entweder in einer Jar-Datei oder in den Class-Dateien enthalten sind. In der Jar-Datei können zusätzlich zu den Java-Klassen auch Bilder und sonstige Bestandteile des Programms abgelegt werden.Die Entwicklung eines MIDlets unterscheidet sich von der Entwicklung eines Applets oder einer Applikation nur in dem geänderten API und in dem leicht abgewandelten Entwicklungsprozess. Der typische Entwicklungsprozess gliedert sich in die folgenden Schritte:
- Umsetzung der Idee in Java mit dem speziellen J2ME API
- Kompilieren der Klassen
- Preverifying
- (Optional) Packen der Klassen in eine Jar-Datei
- Testen der Anwendung
- (Optional) Code-Protection
- Ausführen der Anwendung
Am Anfang steht der Sourcecode
Die Pakete des J2ME APIs entsprechen weitestgehend den wichtigsten Paketen, die schon aus der Java 2- Standard-Edition bekannt sind. Wesentliche Unterschiede gibt es nur bei der grafischen Bedienoberfläche und der IO-Funktionalität. Das Reflection API wurde in der Micro Edition von Java 2 komplett ausgespart, da die Geräte damit nur unnötig belastet wären.Um den Promille-Rechner zu implementieren, sind eine ganze Reihe von Funktionen nötig. Als Erstes muss der Anwender seine persönlichen Daten eingeben, hierzu gehören Geschlecht, Gewicht und der Name des Anwenders. Um das Programm schon für andere Berechnungsgrundlagen vorzubereiten, ist es denkbar auch noch die Größe einzugeben. Sind diese Daten gespeichert, muss das Trinkverhalten nachvollzogen werden können. Menge, Zeitpunkt und Vol% der Getränke sollten möglichst genau eingegeben werden können, um eine präzise Berechnung der BAK zu ermöglichen. Last but not least eine Funktion, um die Blutalkohol-Konzentration zu einem bestimmten Zeitpunkt zu errechnen. Der komplette Ablauf ist in Abpictureung 1 skizziert, nicht berücksichtigt sind hierbei Abbruchfunktionen und das Beenden des Programms.
Die MIDlet-Elemente
Javax.microedition.midlet. ist die Klasse, in der die Methoden zum Starten, Beenden und Unterbrechen des Programms definiert sind. Von dieser Klasse erbt das zentrale Objekt, in dem die komplette Navigation durch die Anwendung definiert und gleichzeitig die Schnittstelle zur Persistenz realisiert ist. Alle persistenten Daten werden im so genannten Record-Management-System gespeichert. Dieses System ist in dem Package javax.microedition.rms untergebracht, dass eine Hand voll Klassen bereitstellt um Records auf dem Datenträger zu speichern und jederzeit wieder abzurufen.Im RMS werden sowohl die persönlichen Daten als auch die getrunkene Alkoholmenge gespeichert. So ist es ohne Probleme möglich, die Anwendung zwischenzeitlich zu verlassen, z.B. um eine SMS zu verschicken, ohne dass vorher eingegebene Daten verloren gehen.
Das GUI
Die komplette Applikation besteht aus vielen Dialogen, die sich größtenteils nur im Detail unterscheiden. Es werden daher nur einige beschrieben, die stellvertretend für die anderen stehen, da eine Beschreibung der kompletten Benutzeroberfläche zu umfangreich ist. Jede Klasse, in der ein Dialog realisiert ist, erbt von einer Klasse aus dem Paket javax.microedition.lcdui. Verwendet werden unter anderem List.class, TextBox.class, Canvas.class und Form.class.Die Klasse Profile1Form.class erbt von TextBox.class und dient zur Eingabe des Namens. Der Dialog wird zum Anlegen und zum Ändern des Profils verwendet. Auf dem Siemens SL45i wird diese Klasse wie in Abpictureung 2 dargestellt. Die Realisierung dieser Klasse ist in Listing 1 zu sehen.
import javax.microedition.lcdui.*;public class Profile1Form extends TextBox implements CommandListener{Promi midlet;Command okCommand = new Command ("OK", Command.OK, 0);Command exitCommand = new Command ("Schließen", Command.OK, 0);public Profile1Form (Promi midlet){super ("Profilname", "", 30, TextField.ANY);this.midlet = midlet;addCommand (okCommand);addCommand (exitCommand);setCommandListener (this);}public void commandAction(Command command, Displayable displayable){if (command == okCommand){String name;name = ((Profile1Form)displayable).getString ();midlet.viewProfile2Form (name);}if (command == exitCommand){midlet.viewMainForm ();}}}
Zusätzlich zum Eingabefeld werden noch die Kommandos OK und Schließen hinzugefügt. Die Implementierung des CommandListeners mit der Methode commandAction verarbeitet dann die Events der beiden Kommandos. Da sich die Formulare gegenseitig nicht kennen, wird das MIDlet-Objekt verwendet um die Navigation abzupictureen. Drückt der Anwender den OK-Button, wird der eingegebene Name ermittelt und an das MIDlet-Objekt weitergegeben. Der Aufruf des nächsten Formulars erfolgt dann aus der aufgerufenen Methode viewProfile2Form heraus (Listing 2).
Listing 2
public void viewProfile2Form (String name){if (name.length () < 3){NotificationForm notificationForm;notificationForm = new NotificationForm (this,"Bitte mindestens 3 Zeichen als Profilnamen wählen");Display.getDisplay(this).setCurrent(notificationForm);} else {aktProfile.setName (name);lastForm = DICT_NEWPROFILE2;if (aktProfile.getGeschlecht()){profile2Form.setSelectedIndex(0, true);} else {profile2Form.setSelectedIndex(1, true);}Display.getDisplay(this).setCurrent(profile2Form);}}
Die Klasse Profile2Form ist eine typische Auswahlliste. Dieser Dialog ermöglicht es, ein Geschlecht auszuwählen und mit OK den nächsten Dialog des Wizards aufzurufen. Auf dem Palm V wird der Dialog wie in Abpictureung 3 dargestellt.
Listing 3
import javax.microedition.lcdui.*;public class Profile2Form extends List implements CommandListener{Promi midlet;Command exitCommand = new Command ("Schließen",1,0);Command okCommand = new Command ("OK",1,0);public Profile2Form (Promi midlet){super ("Geschlecht", List.IMPLICIT);this.midlet = midlet;append ("Männlich",null);append ("Weiblich",null);addCommand (exitCommand);addCommand (okCommand);removeCommand (List.SELECT_COMMAND);setCommandListener ( this );}public String getGeschlecht (){return getString(getSelectedIndex());}public void commandAction(Command command, Displayable displayable){if (command == okCommand){String geschlecht;geschlecht = ((Profile2Form)displayable).getGeschlecht();midlet.viewProfile3Form (geschlecht);}if (command == exitCommand){midlet.viewMainForm ();}}}
Thread-Unterstützung
Rechenintensive Aufgaben und umfangreiche Speicherzugriffe verlangen den momentan verfügbaren Geräten schon eine ganze Menge ab. Auch Siemens hat das erkannt und erhöht beim Start der Java-VM sogar noch die Taktung des Prozessors um die Performance zu erhöhen. Trotz allem gibt es Situationen, in denen das Handy dazu schon mal ein paar Sekunden benötigt - das Speichern eines Profils ist eine solche Situation. Während es im Siemens-Emulator keine Verzögerungen gibt, benötigt das echte SL45i ca. drei bis vier Sekunden um die Daten auf der MultMediaCard zu speichern. Um solche Wartezeiten zu überbrücken, gibt es unter Windows, Linux usw. die von jeher bewährte Sanduhr. Was spricht also dagegen, auch auf dem Handy eine solche Sanduhr anzuzeigen.In dem API des J2ME-Standards ist im Paket javax.microedition.lcdui die Klasse Canvas realisiert, die es ermöglicht, auf dem kompletten Display des Handys pixelgenau zu zeichnen. Es gibt zusätzlich noch gerätespezifische Erweiterungen zum Zeichnen von Sprites und Ähnlichem von Siemens und Nokia. Die Verwendung dieser Möglichkeiten ist aber mit Vorsicht zu genießen, da die Anwendung dann auf den Hersteller oder sogar ein spezielles Gerät reduziert wird (Listing 4).
Listing 4
import javax.microedition.lcdui.*;public class WaitForm extends Canvas implements CommandListener{Promi midlet;Image[] wait;int count = 0;int anz = 9;WaitThread waitThread;public WaitForm (Promi midlet){this.midlet = midlet;wait = new Image[anz];try {for (int i=1; i <= anz; i++)wait[i-1] = Image.createImage("/images/sand"+i+".png");} catch (IOException e) {}}public void setThread (WaitThread waitThread){this.waitThread = waitThread;setPriority(Thread.MAX_PRIORITY)waitThread.appStart = false;waitThread.setPriority (Thread.MAX_PRIORITY);}public void paint (Graphics g){if (waitThread.appStart == false){waitThread.appStart = true;waitThread.start ();}if (isShown() && waitThread.appStart){g.setGrayScale (255);g.fillRect (0,0,getWidth(), getHeight());g.setGrayScale (0);g.drawImage (wait[count],10,0,0);g.setFont (Font.getFont (Font.FACE_SYSTEM,.STYLE_PLAIN,.SIZE_MEDIUM));g.drawString ("please wait",5,65,0);count = (count + 1) % anz;try {Thread.sleep (250);} catch (Exception e){}repaint ();}}}
Speichern der Daten im RMS
Da die unterschiedlichen Geräte nicht alle über eine einheitliche Schnittstelle auf ihr Dateisystem zugreifen, stehen im Package javax.microedition.rms einige abstrakte Klassen zur Verfügung. Hiermit wird der Zugriff auf den Datenträger für die unterschiedlichen Geräte vereinheitlicht. Alle zu speichernden Daten werden im RecordStore gespeichert und können mit Hilfe eines RecordEnumeration-Objekts wieder geladen werden. Der Promille-Rechner speichert unterschiedliche Datensatztypen. Für jeden Benutzer wird das Profil gespeichert und für jedes Getränk eines Benutzers wird ein Datensatz gespeichert. Das Speichern eines Datensatzes für ein Getränk geschieht in der Klasse Alkohol. Hierbei ist zu beachten, dass der zu speichernde String UTF kodiert wird (Listing 5).Listing 5
import javax.microedition.rms.*;import java.util.*;import java.io.*;public class Alkohol extends WaitThread{private String trenner = "#~#";private int promille;private String getraenk;private int menge;private String mengenString;private Date trinkZeit;public byte[] getBytes () throws IOException{String alkMenge = "" + (menge * promille * 79);String sBuffer = alkMenge + trenner + trinkZeit.getTime() + trenner + promille +trenner + getraenk + trenner + menge + trenner + mengenString;ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();DataOutputStream dataOutputStream = new DataOutputStream (byteArrayOutputStream);dataOutputStream.writeUTF (sBuffer);byte[] buffer = byteArrayOutputStream.toByteArray ();return buffer;}public void save () throws RecordStoreException, IOException{RecordStore recordStore;recordStore = RecordStore.openRecordStore ("profile_" + profile.getId(),true);byte[] buffer;buffer = getBytes ();recordStore.addRecord(buffer,0,buffer.length);recordStore.closeRecordStore ();}}
Kompilieren des MIDlets
Kompilieren eines Midlets unterscheidet sich nur geringfügig vom Übersetzen einer gewöhnlichen Java- Anwendung. Da das API des JDK nicht gültig ist, wird dem Java-Compiler die Jar-Datei des gültigen API übergeben:c:> javac -bootclasspath c:\Pfad\zu\den\MIDP\Klassen Klasse.class
In der Regel besteht eine Anwendung aus mehreren Klassen, die dann zu einer Jar-Datei zusammengefasst werden können. Das Kommando jar kann wie gewohnt auf die fertigen Klassen losgelassen werden:
c:> jar cvf promille.jar Klasse1.class Klasse2.class Klasse3.class ....
Der Application-Descriptor muss die Erweiterung .jad haben und die folgenden Attribute beinhalten:
MIDlet-Version: Midlet-Version
MIDlet-Vendor: Entwickler des Midlets
MIDlet-Name: Name des Midlets
MIDlet-jar-URL: Die URL unter der die Jar-Datei heruntergeladen werden kann
MIDlet-Jar-Size: Die Anzahl der Bytes der Jar-Datei
MIDlet-1: Dieses Attribut besitzt die Parameter Name, Icon und Klasse
Zusätzlich können noch die folgenden Attribute angegeben werden:
MIDlet-Description: Beschreibung des MIDlets
MIDlet-Icon: Das Icon des Midlets
MIDlet-Info-URL: URL unter der weitere Infos zu dem MIDlet erhältlich sind.
MIDlet-Data-Size: Die minimale Anzahl der Bytes auf dem Datenträger, die von dem Midlet benötigt werden.
Alle anwendungsspezifischen Attribute dürfen nicht mit MDlet- beginnen.
Testen, testen und nochmals testen
In den seltensten Fällen hat man alle Geräte zur Hand, auf denen die Anwendung später ausgeführt werden sollen. Trotz allem ist das Testen einer der wichtigsten Punkte im Softwareentwicklungsprozess - glücklicherweise gibt es die unterschiedlichsten Emulatoren von den Geräteherstellern und von Sun. So gibt es z.B. Emulatoren von Siemens, Nokia, Motorola und Palm, die es ermöglichen die Anwendung auszuführen, ohne entsprechende Hardware anschaffen zu müssen.Der Test eines MIDlets mit dem Siemens-Emulator ist denkbar einfach. Nach der Installation befindet sich im Installationspfad ein Ordner, in dem sich die Inhalte der Multimedia-Karte des realen Handys befinden. Somit auch die MIDlets, die unter \mmc\java\jam abgelegt werden. In diesem Verzeichnis wird ein neuer Ordner angelegt und sowohl die Jar-Datei als auch die Jad-Datei darin abgelegt. Dabei sollte darauf geachtet werden, keine Umlaute oder Sonderzeichen in dem Verzeichnisnamen zu verwenden, da die Java-VM des Siemens SL45i hiermit noch Probleme hat.
Java Bytecode Protection
Bis auf die Kommentare kann aus den Klassen in der Jar-Datei praktisch der komplette Sourcecode reproduziert werden. Dies zu verhindern ist mit dem Freeware Tool RetroGuard, dass unter der GPL vertrieben wird, möglich. Der Obfuscation-Prozess entfernt alle unnötigen Informationen aus den Klassen. Weiterhin werden Namen von Klassen, Methoden und Variablen in bedeutungslose und möglichst kurze Bezeichner geändert, ohne die Anwendung nach außen zu verändern - hierdurch wird der dekompilierte Java-Code sehr schwer lesbar. Netter Nebeneffekt ist, dass die Klassen sehr viel kleiner werden und Jar-Dateien bis zu 50 Prozent weniger Speicher verbrauchen. Gerade bei stark eingeschränkten Ressourcen wie z.B. in dem Siemens SL45i oder dem Nokia 7650, die nur zwischen 125kB und 150kB Speicher für eine Java-Anwendung bereitstellen, ist ein geringer Speicherverbrauch sehr wichtig.Bevor RetroGuard ausgeführt wird, muss noch ein Skript für das MIDlet erzeugt werden. In dieser Konfiguration wird dem Tool mitgeteilt, welche Teile der Klassen nicht geändert werden sollen. Normalerweise ist dies die Methode main(String argv[] ) einer Applikation oder die Methoden start(), stop(), destroy(), init() eines Applets. Bei einem MIDlet sind es die Methoden startApp(), pauseApp() und destroyApp() der MIDlet- Klasse. Das komplette Skript für RetroGuard sieht folgendermaßen aus:
## Scriptname: promi.rgs# Script for RetroGuard bytecode obfuscator.# JAR-Datei: promi.jar#.class Promi.method Promi/startApp ()V.method Promi/destroyApp (Z)V.method Promi/pauseApp ()V
java -classpath API.jar;retroguard.jar RetroGuard promi.jar promiRG.jar promi.rgs
Wenn von Anfang an klar ist, dass die Klassen mit einem Obfuscation-Tool geschützt werden, kann das Preverify-Kommando vor dem Packen mit jar übersprungen werden, da die Änderungen vom Retroguard ignoriert werden. Die Datei promiRG.jar ist jetzt nur noch 39kB groß. Im Vergleich zu der 49,1kB großen Datei promi.jar ist das eine Ersparnis von etwas über 20 Prozent. Die relativ geringe Ersparnis ergibt sich durch die Bilddateien in der Jar-Datei, die nicht verändert werden. Trotzdem ist dies schon ein merklicher Unterschied, wenn die Anwendung per WAP über das Over The Air (OTA)-Protokoll heruntergeladen wird.
Fazit
Die technischen Voraussetzungen für die Programmierung moderner Handys in Java sind erfüllt. Bleibt abzuwarten, wann sich diese Technologie durchsetzt - ob Handys demnächst sogar die PDAs ersetzen oder MIDlets nur eine Modeerscheinung sind. Ich persönlich glaube, dass Handys und PDAs nebeneinander existieren werden. Genauso wie programmierbare Handys, wird es in Zukunft auch immer mehr PDAs mit eingebautem Mobiltelefon geben. MIDlets werden dann die Programme sein, die sowohl auf Handys als auch auf PDAs ausgeführt werden können. Auch die Rechenleistung der Geräte wird in Zukunft weiter verbessert, was eine Voraussetzung für immer anspruchsvollere Anwendungen ist; die Vorteile der MIDlets gegenüber WAP liegen klar auf der Hand. Trotzdem bin ich der Meinung, dass WAP von dieser Technologie profitieren wird, da sich WAP und J2ME gegenseitig unterstützen und dadurch eine breitere Akzeptanz gewährleisten.P.S.: Don't Drink And Drive.
Links und Literatur
- [1] Persistente Datenspeicherung in der J2ME: Javamagazin 12.2001
- [2] Tatzeit BAK: http://www.bads.de/tatzeit-bak.htm
- [3] Palm: www.palm.com
- [4] Sun Wireless Toolkit: java.sun.com/products/j2mewtoolkit/
- [5] Sun Mobil Developer: wireless.java.sun.com
- [6] Forum Nokia: www.forum-nokia.com
- [7] Siemens Mobil: www.siemens-mobile.com
- [8] RetroGuard: www.retrologic.com




