Not freaky, just geeky

Das gefesselte Wunderkind: Java auf dem iPhone

Christian Müller

Das iPhone ist wahrlich ein gefesseltes Wunderkind. In diesem Artikel wird gezeigt, wie man diese Fesseln löst und welche Schritte unternommen werden müssen, um eine selbstgeschriebene Java-Anwendung auf dem iPhone zu kompilieren und auszuführen.

Mit dem iPhone/iPod Touch hat Apple eine Geräteserie auf dem Markt gebracht, die nicht nur wegen ihres Designs, sondern auch durch die intuitiv zu bedienende Oberfläche schon nach kurzer Zeit sehr beliebt war. Die Serie erfährt zurzeit einen richtigen Hype. Betrachtet man sich allerdings die Geräte näher, stellt man leider sehr schnell fest, dass man vom eigentlichen System gar nichts zu sehen bekommt. Die Verwaltung sämtlicher auf dem Gerät befindlicher Daten findet ausschließlich über iTunes statt. Für bestimmte Benutzergruppen ist dies sicherlich sinnvoll, aber ich als Entwickler will mehr mit meinem Gerät anstellen als nur die Apps zu starten und Icons hin und her zuschieben.

iPhone OS

Auf dem iPhone und dem iPod Touch liegt mit dem Betriebssystem iPhone OS eine weitgehend gleiche technische Grundlage vor. Bei diesem Betriebssystem handelt es sich im Wesentlichen um eine auf minimaler Hardware (ARM-Prozessor) lauffähigen, angepassten und deutlich verkleinerten Version Mac OS X.

Apple stellt auch ein kostenloses SDK zur Verfügung. Zurzeit in der Version 3.2 erhältlich, setzt dieses voll auf die Sprache Objective C, die seit der Markteinführung des iPhone ebenso einen Hype erlebt. Als IDE liefert Apple Xcode mit und rundet das SDK außerdem mit einem iPhone Simulator ab.

Auf den meisten iPhone- sowie iPod-Geräten dürfte derzeit wohl eine Version 2.x oder 3.1.x vorzufinden sein. Auf dem neuen iPad kommt ebenso das iPhoneOS zum Einsatz, hier hat Apple allerdings den Versionszähler auf 3.2 erhöht. Zwischen den Betriebssystemen, beziehungsweise den einzelnen Versionen von iPhoneOS auf iPhone und iPod, gibt es allerdings doch feine Unterschiede. So ist zum Beispiel beim iPod keine Telefonfunktion enthalten, auch die Bluetooth-Unterstützung, die ab der zweiten Generation des Touch zwar hardwareseitig verfügbar ist, wird leider vom iPhoneOS (Version 2.2.1) standardmäßig nicht unterstützt. Hier kann aber mit ein paar Tricks nachgeholfen werden. Ab der OS-Version 3.0 gibt es auch für den iPod (ab 2G) entsprechenden Bluetooth Support. Die letzte, mit Erfolg getestete Version für die Java-Entwicklung war das iPhoneOS in der Version 2.2.1 oder kleiner. Für neuere Versionen gibt es derzeit leider noch keine stabile Unterstützung der grafischen Komponenten.

Jailbreak

Besorgt man sich ein iPhone oder einen iPod direkt vom Händler, kann man leider, wie bereits weiter oben erwähnt, nur mit iTunes Programme laden und seine Daten verwalten. Mit einigen Modifikationen an der Firmware, dies nennt man „jailbreaken“, steht dem Benutzer jedoch eine 

Vielzahl neuer Möglichkeiten offen: Einsicht in das komplette Dateisystem, kostenlose Apps aus dem Paketmanager (Cydia), ja sogar Root-Rechte auf das komplette Gerät. Was will man mehr? Beim iPhone lässt sich so auch der SIM-Lock umgehen und das Gerät für andere Mobilfunknetze freischalten (Unlock). Allerdings sollte man sich hier im Klaren darüber sein, dass man beim Jailbreaken alle seine Daten und Einstellungen verlieren kann, eine vorherige Datensicherung ist also in jedem Fall vorzunehmen. Wichtig ist auch, dass Apple keinerlei Garantie auf ein Gerät gewährt, dass durch einen Jailbreak geöffnet wurde. Ein Rückstellen der Software auf den Originalzustand ist durch ein einfaches Firmware-Upgrade über iTunes jederzeit möglich.

Das Jailbreaken an sich geht wie folgt von dannen: Man öffnet das Programm, z.B. blackra1n [1] und wird aufgefordert, die zurzeit verwendete Firmware-Datei (zum Beispiel iPod1,1_2.2.1_5H11_Restore.ipsw) auszuwählen. Nachdem diese geprüft wurde, bearbeitet das Programm die Firmware und im Anschluss wird man aufgefordert, sein Gerät in den sogenannten DFU-Mode zu versetzen. Der DFU-Mode ist nicht zu verwechseln mit dem Restore-Mode.

Im sogenannten Restore-Mode wird das iPhone teils wieder hochgefahren (Stecker- und iTunes-Symbol erscheinen), endet aber im Wartungszustand und kommuniziert mit iTunes. Der DFU-Mode hingegen ist auf Hardware-Basis ein Ping an iTunes, dass sich das iPhone am Computer befindet. Es kommuniziert aber nicht weiter mit iTunes und der Bildschirm bleibt schwarz.

Um sein Gerät in den DFU-Mode zu versetzen, folgt man den Schritt für Schritt-Anweisungen des Programms. Ich empfehle das Programm blackra1n [1], da dieses selbsterklärend und einfach zu bedienen ist. Wurden die Anweisungen befolgt, beginnt das „Flashen“ der Firmware. Dies kann bis zu 10 Minuten dauern. In dieser Zeit sollte das Gerät nicht ausgeschaltet werden, ein voller Akku ist anzuraten. Nach einem Neustart des Geräts steht automatisch ein Debian-Paketmanager mit grafischer Oberfläche (Cydia) zur Verfügung, welcher Zugriff auf etliche Freeware-Programmquellen mitbringt. Aus diesen können .deb-Pakete (wichtig: nur für die Prozessorarchitektur ARM) am Gerät selbst, über Wireless LAN oder direkt via Internet, wie zum Beispiel ein Terminal im Bash Style (Mobile-Terminal, siehe Abbildung 3), Open SSH, VNC Server (Veency), Finder (MAC Explore, siehe Abbildung 1), SBSetting (Taskmanager und mehr…), Winterboard (OS-Themes), JamVM (Java Interpreter für iPhone/iPod) installiert werden.

Abbildung 1: iPhone MobileFinder im Root- Verzeichnis

[ header = Seite 2: Not freaky, just geeky! ]

Not freaky, just geeky!

Mit diesem Artikel möchten wir auf JAXenter eine Serie zum Mitmachen starten. Sinn und Zweck dieser Serie ist es, auch mal völlig neben der Spur, soll heißen, auch mal abseits der üblichen ernsthaften und seriösen Enterprise-Entwicklung mit Java über Dinge berichten zu können, die auf der einen Seite zwar verrückt sind, aber trotzdem mit Java zu tun haben. Wir glauben, dass eine Serie wie diese nicht nur Spaß ist, sondern auch Spaß macht, weil sie auf amüsante Weise Inhalte vermittelt, bei denen man trotzdem noch etwas dazulernen kann. Mitmachen bedeutet, dass wir jeden Leser recht herzlich dazu einladen wollen, sich an dieser Serie zu beteiligen! Wer also schon mal Verrücktes mit Java gemacht hat, zum Beispiel eine Steuerung der heimischen Heizungsanlage in Java oder das Ansprechen von exotischen Datenbanken über COM und Microsoft ADO (weil es keinen JDBC-Treiber gibt) oder das Laden von Kontoauszügen über HBCI von einem Linux-System mittels JNI und JRuby oder die Entwicklung einer Eclipse-RCP-Anwendung komplett in Scala… der ist hier genau richtig! Wir würden uns freuen, wenn sich der eine oder andere Leser jetzt angesprochen fühlt und diese Serie mit einem weiteren ungewöhnlichen aber doch interessanten Artikel bereichert.

Pre-Installation für Java & Co.

Bevor man mit der Java-Entwicklung auf dem iPhoneOS beginnen kann, müssen einige zusätzliche Pakete auf dem Gerät installiert werden. Dies erfolgt mit dem bereits erwähnten Paketmanager Cydia. Für Java-Entwicklung dringend erforderlich sind alle in Tabelle 1 aufgeführten Pakete. An dieser Stelle wird empfohlen, zusätzlich noch folgende hilfreiche Pakete mit zu installieren: OpenSSH kann die Arbeit wesentlich erleichtern, weil man so ein Root-Terminal (mit dem Password „alpine“) direkt auf den Desktop holen kann. Aber auch ein Terminal auf dem Gerät selbst (Mobile-Terminal, siehe Abbildung 3) kann gelegentlich hilfreich sein. SBSettings, eine Art Taskmanager, ist sehr nützlich, wenn man z.B. Akkuleistung sparen will, um bestimmte Hardware auszuschalten oder laufende Prozesse zu beenden. Sogar ein Neustart des SpringBoards (diese Anwendung ist zuständig für die grafische Oberfläche) ist damit schnell zu realisieren. Um Daten zwischen einem Computer und dem iPhoneOS auszutauschen, gibt es den iPhone Browser [2] (Abbildung 2), der bei einem geflashten Gerät über das USB-Datenkabel, Zugriff auf das komplette Dateisystem bietet. Alternativ gibt es noch die Möglichkeit, sich per Wireless LAN mit WinSCP [5] über OpenSSH mit dem Gerät zu verbinden.

Abbildung 2: iPhone Browser Filesystem

Tabelle 1: zu installierende Pakete

[ header = Seite 3: Hello iPhone! ]

Hello iPhone!

Das erste Programm soll die allseits bekannte HelloWord-Applikation werden. Hierzu erstellen wir ein neues Java-Projekt und fügen eine Klasse mit dem Namen HelloJava hinzu. Listing 1 zeigt die Klasse HelloJava.java und Abbildung 3 die Ausgabe auf einem iPod Touch(OS v2.2.1).

public class HelloJava {
	
	public static void main(String[] args) {
	System.out.println("Hello iPhone!");
	System.out.println("OS:" + System.getProperty("os.name") +
	" version:" + System.getProperty("os.version") +
	" arch:" + System.getProperty("os.arch") );
	}
	}
	

Abbildung 3: HelloWorld Terminalausgabe

Nachdem die Java-Quellcodedatei mit dem iPhone-Browser oder WinSCP auf das Gerät kopiert wurde, kann sie mit dem Befehl jikes HelloJava.java im Terminal kompiliert werden. Die dabei entstandene Class-Datei kann mit java HelloJava ausgeführt werden.

Datenschaufeln

Da das iPhoneOS alle Kontaktdaten, den Anrufverlauf, den Kalender, sogar SMS und Voicemailheader in einer SQLite-Datenbank hinterlegt, ist es mit Java auch sehr einfach möglich, auf diese Daten zuzugreifen. Tabelle 2 zeigt die wichtigsten SQLite-Datenbanken im Überblick. Um die Datenbanken zu öffnen, verwendet man am besten den SQLite DB Browser [4], der kostenlos auf sourceforge.net zum Download bereitsteht.

Tabelle 2: SQLite-Datenbankfiles mit Pfaden

Ist das Paket „Java SQLite“ mit dem Paketmanager installiert, befinden sich die entsprechenden .jar-Dateien in Verzeichnis /var/stash/share.xxxxxx/java/sqlite.jar auf dem Gerät. Bindet man diese nun in sein Java-Projekt ein, ist es möglich, eine Datenbankverbindung aufzubauen und über SQL entsprechende Abfragen auszuführen (siehe Listing 2).

	SQLite.Database ab = new SQLite.Database();
	ab.open(userHomeDirectory().toString() + "/Library/AddressBook/AddressBook.sqlitedb", 0666);
	SQLite.Stmt st = ab.prepare("select first,last,value from abperson p, abmultivalue v where v.record_id=p.rowid and v.label=1 and p.first is not null order by first");
	
	while (st.step())
	contacts_.add(new Contact(st.column_string(0), st.column_string(1), st.column_string(2)));
	

Abbildung 4: Aufbau einer iPhone-Java-Anwendung

[ header = Seite 4: iPhone-UIs mit Java? ]

iPhone-UIs mit Java?

Alle iPhoneOS-Programme folgen den gleichen Aufbau, der sich in Abbildung 4 darstellt. Das Bild Default.png (480×320 Pixel) wird für den Hintergrund der Applikation verwendet. icon.png (60×60 Pixel) dient als Programmicon auf dem SpringBoard und die Datei Info.plist beschreibt, typisch für Mac OS X, die Programmparameter in XML. Der Aufbau dieser XML-Datei ist in Listing 3 abgebildet, die wichtigsten Parameter finden sich im Kasten „Parameter aus Info.plist“.

Die wichtigsten Parameter aus Info.plist
  • „CFBundleDevelopmentRegion“ – Identifiziert die Sprache der Applikation.
  • „CFBundleExecutable“ – Der Name der ausführbaren Datei, welche sich im selben Ordner wie die Info.plist befindet.
  • „CFBundleIdentifier“ – Ein eindeutiger Key, um die Applikation zu identifizieren.
  • „CFBundleName“ – Der Name der Applikation, welcher im SpringBoard dargestellt wird.

Das Shell-Skript SampleJava sorgt dafür, dass beim Ausführen die Librarys sqlite.jarjocstrap.jar und uicaboodle.jar unter /usr/share/java/ gefunden werden und die Mainmethode untercom.saurik.uicaboodle.Main die eigene Javaklasse aufruft. Um keine Classpath- Probleme mit der JVM zu bekommen, wird die /usr/bin/java-Executable in das Programmverzeichnis verlinkt.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" „http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>SampleJava</string>
<key>CFBundleIdentifier</key>
<string>IPodJava</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>IPodJava</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
</dict>
</plist>
	

Um die grafischen Komponenten mit Java anzusprechen, benötigt man zusätzlich die beiden Librarys jocstrap.jar und uicaboodle.jar, die bei der Installation der Pakete in Tabelle 1 automatisch mitgeliefert werden. Bindet man diese in sein Java-Projekt ein, besitzt man Zugriff auf die grafischen Komponenten des Betriebssystems. Im folgenden Beispiel wird eine einfache Oberfläche mit einer Table erzeugt, in der die Kontakte aus der SQLite-Datenbank dargestellt werden. Listing 4 zeigt die Programmklasse, welche von der Klasse UIApplication erbt. Die Entitäten Contact undSection sind POJOs, die über ihren Konstruktor gefüllt werden. Wenn dieses Programm gestartet wird, wird die Methode applicationDidFinishLaunching aufgerufen, welche eine Datenbankverbindung öffnet und die Daten in eine Array-Liste füllt. Danach wird die Größe der Fenster festgelegt, der Fenstertitel gesetzt, die Anfangsbuchstaben der Einträge als Sectionsdefiniert und zusammengesetzt. Um die Daten letztlich anzuzeigen, wird eine Tabelle mit einer Spalte konfiguriert. Die Zellen der Spalte stellen die Kontakte dar, welche durch die Methode table in die Zellen finden, sobald die Spalte mit Identifier an der Tabelle angemeldet wird. Zuletzt wird die bearbeitete Sectionliste neu geladen.

import static joc.Static.NO;
import static joc.Static.YES;

import java.util.ArrayList;

import joc.*;
import obc.*;

public class IPodJava extends UIApplication {

private UIWindow window;

private ArrayList<Contact> contacts_;
private ArrayList<Section> sections_;

@Message
int numberOfSectionsInSectionList$(UISectionList list) {
return sections_.size();
}
@Message
String sectionList$titleForSection$(UISectionList list, int section) {
return sections_.get(section).title;
}
@Message
int sectionList$rowForSection$(UISectionList list, int section) {
return sections_.get(section).row;
}
@Message
int numberOfRowsInTable$(UITable table) {
return contacts_.size();
}
@Message
UITableCell table$cellForRow$column$reusing$(UITable table, int row, UITableColumn col, UITableCell reusing) {
Contact contact = contacts_.get(row);
UIImageAndTextTableCell cell;

if (reusing != null){
cell = (UIImageAndTextTableCell) reusing;
} else {
cell = (UIImageAndTextTableCell) new UIImageAndTextTableCell().init();
}
	
cell.setTitle$(contact.getName());
return cell;
}
@Message
public byte table$canSelectRow$(UITable table, int row) {
return YES;
}
@Message
public void applicationDidFinishLaunching$(Object unused) throws Exception {
contacts_ = new ArrayList<Contact>();
sections_ = new ArrayList<Section>();
	
SQLite.Database ab = new SQLite.Database();
ab.open(userHomeDirectory().toString() + "/Library/AddressBook/AddressBook.sqlitedb", 0666);
try {
SQLite.Stmt st = ab.prepare("select first, last from abperson where first is not null order by first");
try {
while (st.step())
contacts_.add(new Contact(st.column_string(0), st.column_string(1)));
} finally {
st.close();
}
} finally {
ab.close();
}
	
CGRect outer = UIHardware.$fullScreenApplicationContentRect();
window = new UIWindow().initWithContentRect$(outer);
	
window.orderFront$(this);
window.makeKey$(this);
window._setHidden$(NO);
	
CGRect inner = window.bounds();
CGSize navsize = UINavigationBar.$defaultSize();
CGRect navrect = new CGRect(0, 0, inner.size.width, navsize.height);
	
UIView view = new UIView().initWithFrame$(inner);
window.setContentView$(view);
	
UINavigationBar navbar = new UINavigationBar().initWithFrame$(navrect);
view.addSubview$(navbar);
navbar.setBarStyle$(1);
	
UINavigationItem navitem = new UINavigationItem().initWithTitle$("Kontakte");
navbar.pushNavigationItem$(navitem);
	
char letter = 0;
for (int i = 0; i != contacts_.size(); ++i) {
String name = contacts_.get(i).getName();
char now = name.charAt(0);
if (letter != now) {
letter = now;
sections_.add(new Section(i, new String(new char[] { now })));
}
}
	
CGRect lower = new CGRect(0, navsize.height, inner.size.width, inner.size.height - navsize.height);
UISectionList list = new UISectionList().initWithFrame$(lower);
view.addSubview$(list);
	
UITableColumn col = new UITableColumn().initWithTitle$identifier$width$("Name", "name", 320);
	
UITable table = (UITable) list.table();
table.setSeparatorStyle$(1);
table.addTableColumn$(col);
table.setReusesTableCells$(YES);
	
list.setDataSource$(this);
list.reloadData();
}
	
}
	
Fazit

Alles in allem betrachtet ist die Java-Entwicklung auf dem iPhoneOS eine sehr unausgereifte und mühselige Angelegenheit. Solange Apple, beziehungsweise Sun/Oracle, keine JVM für das iPhone anbietet, werden Java-Programme nur mit einem Jailbreak lauffähig sein. Desweiteren gibt es keine Dokumentation zu den Bibliotheken JocStrap und UICaboodle, die notwendig sind, um die grafischen Komponenten des Betriebssystems anzusprechen. Auch wenn man mit seinem iPhoneOS auf dem aktuellen Stand bleiben will, wird man mit der Java-Entwicklung keine Freude haben, da bei jeder Versionsänderung die JNI-Zugriffe überarbeiten werden müssen.

Die Quintessenz lautet also: Wer produktive Software für das iPhoneOS schreiben will, wird am iPhone SDK und an Objective C wohl nicht vorbeikommen.

Wichtiger Hinweis: Alle hier im Artikel beschriebenen Vorgänge erfolgen vollständig auf eigene Gefahr. Für Beschädigungen am Gerät oder Datenverlust ist weder der Autor noch der Verlag verantwortlich. Durch Jailbreaken erlischt jeglicher Garantieanspruch von Apple. Ein Einsenden von derart modifizierten Geräten, ohne die Originalsoftware wieder hergestellt zu haben, ist nicht möglich! Vorher unbedingt Originalsoftware zurückspielen! Es gibt auch Gerüchte, dass die GSM-Leistung des iPhones verschlechtert wird und somit der Netzempfang beeinträchtigt werden kann. Es kann auch vorkommen, dass die Akkuleistung nach dem Jailbreak verschlechtert wird. Nicht von der Hand zu weisen ist die Tatsache, dass sich durch den Jailbreak iPhone und iPod über SSH erreichen lassen und das Standardpasswort bekannt ist. Dies führte nach kurzer Zeit zu einer schnellen Verbreitung eines speziellen iPhone-Virus. Rechtlich ist es so, dass der Besitzer eines iPhone/iPod grundsätzlich alles damit machen kann, was er möchte. Apple hat speziell hierfür in den USA eine 27 Seiten starke Erklärung im Copyright Office eingereicht [6], die klarmachen soll, welche Einschränkungen und Probleme durch Jailbreaken entstehen können. Dieses Dokument wird derzeit jedoch als Diskussionsgrundlage gesehen und nicht als Antrag oder juristischer Schritt seitens Apple.
Christian Müller arbeitet als Software-Entwickler bei der hama GmbH & Co. Sein Schwerpunkt liegt hier bei der Entwicklung von Anwendungen auf Basis von Eclipse RCP, OSGi und Spring.
Geschrieben von
Christian Müller
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.