LEHRSTUHL FÜR MEDIZINTECHNIK DER FAKULTÄT FÜR MASCHINENWESEN DER RWTH AACHEN Univ.-Prof. Dr.-Ing. Klaus Radermacher Seminararbeit Entwicklung mobiler Applikationen mit Android Vorgelegt von Moritz Hübner Matr.-Nr.: 833452 Fakultätsinterner Betreuer: Prof. Dr. rer. nat. V. Sander, FH Aachen Betreuender wissenschaftlicher Mitarbeiter: Dr.-Ing. M. de la Fuente Ort, Datum i Inhaltsverzeichnis 1. Motivation........................................................................................................................ 1 2. Einführung ...................................................................................................................... 2 2.1 Was ist Android? 2 2.2 Geschichte 2 3. Entwicklung..................................................................................................................... 4 3.1 Entwicklungsumgebung Eclipse 4 3.2 Projekterstellung mit Eclipse 4 3.3 Analyse des „Hello Android“ Projekts 4 3.3.1 Android Manifestdatei 5 3.3.2 Die Activity 6 3.3.3 Projektaufbau 6 3.3.4 Die Klasse R.java 7 3.4 Komponenten 7 3.4.1 Activities 7 3.4.2 Kommunikation durch Intents und Intent-Filter 9 3.4.3 Broadcast Receiver 11 3.4.4 Fragments 13 3.4.5 Services 14 3.5 Resourcen und ihre Verwaltung 17 3.6 Views und Viewgroups 18 3.7 Multitasking 19 3.8 SQLite 20 3.8.1 Arbeiten mit der Kommandozeile 20 3.8.2 Datenbankzugriff in der App 21 3.8.3 Cursor 22 3.9 Content Provider 22 4. Techniken ...................................................................................................................... 24 4.1 Debugging 24 4.1.1 LogCat 24 4.2 Auslesen von Höhe und Breite eines Layouts 24 4.3 Drag and Drop 25 5. Fazit und Ausblick ........................................................................................................ 27 Literatur- und Quellenverzeichnis .......................................................................................... I Anhang .................................................................................................................................... III i Motivation 1. 1 Motivation Ziel dieser Arbeit ist es eine Einführung zur Entwicklung von Applikationen mit dem „Android Framework“ zu geben, um dem Leser die Möglichkeit zu bieten selbst komplexere Android Applikationen zu erstellen. Sobald man sich z.B. im Rahmen eines neuen Projekts mit einer neuen Technologie, wie in diesem Fall mit Android, auseinandersetzt, stellt sich die Frage nach Umsetzungsmöglichkeiten und den Leistungsgrenzen des benutzten „Frameworks“. Nachdem die Anforderungen geklärt sind und dort z.B. ein „Drag and Drop“ Modul gewünscht ist muss man in eben dieser Technologie herausfinden wie genau die bereitgestellten Mechanismen funktionieren, um das Geforderte umzusetzen. Dies kann mitunter sehr mühsam sein. Diese Arbeit soll für solche Fragestellungen eine Anlaufmöglichkeit bieten. 1 Einführung 2. 2 Einführung Im Folgenden wird eine Einführung darüber geben, was Android ist und was es ausmacht, sowie eine kurze Zusammenfassung seiner Geschichte geliefert. 2.1 Was ist Android? Android ist kein Betriebssystem sondern ein Begriff für einen Stapel aus verschiedener Software [Android 2011]. Zu Android gehören die Standardanwendungen wie ein Anwendungsstarter mit Widgetunterstützung, eine Kontaktdatenbank, eine Uhr, ein Browser, ein Email Client, sowie oft der Android Market zum Kauf zusätzlicher Software (das Amazon Kindle Fire wird ohne Android Market ausgeliefert, bietet dem Nutzer aber die Möglichkeit den Market nachzurüsten [Murph 2011]). Nahezu jede Anwendung, auch die Standardanwendungen, werden in der virtuellen Maschine Dalvik ausgeführt. Eine Ausnahme stellen Teile von Apps dar, die mit dem NDK (Native Development Kit) geschrieben wurden (s. Anhang NDK). Dalvik übersetzt alle Quelltexte in Dalvik Executables, die das Programm dann auf dem Betriebssystem ausführt. Als Betriebssystem wird das freie Linux mit dem Kernel 2.6 verwendet. Linux stellt die Abstraktionsschicht zwischen Hardware und der übrigen Plattform dar und stellt Systemdienste wie Sicherheit, Speicher- und Prozessverwaltung, Treibermodell und Netzwerkstapel zur Verfügung [Künneth 2011]. 2.2 Geschichte Android geht auf die Firma Android Inc. von Andy Rubin zurück. Die Firma wurde 2003 gegründet und beschäftigte sich fortan mit der Entwicklung der freien Plattform Android [Kowitt 2011]. Andy Rubin war Softwareentwickler bei Apple Inc. und später bei General Magic wo er bereits an einem Betriebssystem für mobile Geräte mit grafischer Benutzeroberfläche arbeitete [Künneth 2011]. Außerdem war Rubin maßgeblich an der Auslieferung des ersten kabellosen PDAs („personal digital assistant“) beteiligt [Stanford 2011]. 2005 wurde Android Inc. von Google gekauft und die Entwicklung konsequent unter Rubins Leitung fortgesetzt. 2007 wurde Android offiziel angekündigt und Entwickler mit Preisgeldern und dem zu diesem Zeitpunkt verfügbarem SDK gelockt, Applikationen für Android zu entwickeln. Zu dieser Zeit trat die Open Handset Alliance (OHA) das erste Mal an die Öffentlichkeit. Diese Allianz war ein Zusammenschluss aus 34 Firmen aus den Bereichen 2 Einführung 3 Entwicklung, Netzbetrieb und Geräteherstellern unter der Führung von Google. Ihr Ziel war es mit Android ein freies Produkt zu entwickeln, welches Benutzerfreundlichkeit, kostengünstige Entwicklung und Verteilung vereint. Dadurch, dass es sich um freie quelloffene Software handelte, hatten alle Beteiligten der OHA von Anfang an die Möglichkeit, die Plattform anzupassen und zu erweitern. Ein Jahr später wurde das erste Smarthphone von T-Mobile, das G1 mit Android auf den Markt gebracht und lief mit Android 1.1. Zu diesem Zeitpunkt war Android die einzige Konkurrenz für Apples iPhone. Heute ist die OHA auf ca. 80 Firmen angewachsen [Künneth 2011]. 3 Entwicklung 3. 4 Entwicklung Kapitel drei befasst sich mit der Entwicklungsumgebung Eclipse und grundlegenden Komponenten der Android Plattform. Es werden Code Beispiele während den Abschnitten angegeben um das Erklärte zu unterstreichen. Diese Arbeit behandelt die Vorgänge in der Entwicklungsumgebung Eclipse. Es gibt Alternativen wie z.B. Intellij, auf diese soll in dieser Arbeit allerdings nicht eingegangen werden. 3.1 Entwicklungsumgebung Eclipse Zur Entwicklung von Android Applikationen kann Eclipse mit dem ADT (Android Development Tools) Plugin, dem Android SDK (Software Development Tool) und dem JDK (Java Development Kit) verwendet werden. Das Android SDK bietet die Möglichkeit ein Android Endgerät (Tablet oder Smartphone) mit dem mitgelieferten Emulator zu emulieren oder eine Applikationen direkt auf einem Endgerät zu testen. Dafür muss der google-usb_driver auf dem Entwicklungssystem installiert und auf dem Gerät unter Einstellungen, USBDebugging aktiviert sein. Der USB Treiber kann mit dem Android SDK Manager heruntergeladen werden. Der Manager versorgt die Plattform mit den neuesten Updates, wie z.B. mit Android 4.0 (API Level 14, Stand November 2011). 3.2 Projekterstellung mit Eclipse Um ein Projekt mit Eclipse zu erstellen, wählt man über FILE • NEW • ANDROID PROJECT aus. Im folgenden Dialog gibt man einen Projektnamen an und klickt auf Next. Man könnte noch zusätzliche Einstellungen machen, wie z.B. ein Projekt von einer existierenden Quelle erzeugen. Für ein neues Projekt ist jedoch der oben beschriebene Weg ausreichend. Im nächsten Schritt wählt man dann die Android Version aus unter der das Projekt laufen soll, dies sollte sich am benutzten Endgerät orientieren, da nicht alle Hersteller für ihre Geräte Updates auf die neuesten Android Versionen anbieten. So wird ein minimales lauffähiges Android Projekt erstellt, welches den Nutzer mit einem „Hello Android“ begrüßt. 3.3 Analyse des „Hello Android“ Projekts Als nächstes wird das „Hello Android“ Projekt, bezüglich Aufbau und Funktion, näher beschrieben. 4 Entwicklung 5 3.3.1 Android Manifestdatei Die Manifestdatei ist eine XML Datei, welche in jeder Android Applikation vorhanden ist. Sie stellt die zentrale Beschreibungsdatei dar. Sie gibt Bestandteile des Programms an, z.B. alle Activities, Services, Content Provider oder Broadcast Receiver. Außerdem kann der Benutzer den aktuellen Versionsnamen und den Versionscode der App einsehen. Eine Manifestdatei könnte z.B. so aussehen: Abbildung 1: Beispiel einer Manifestdatei Android liefert eine grafische Oberfläche zur Bearbeitung der Manifestdatei. Man kann alle Änderungen über diese Oberfläche oder direkt in der XML Datei eintragen. Die Manifestdatei bietet außerdem eine Rechteverwaltung und Einstellungen zu den Endgeräten. Zu erwähnen sind in diesem Zusammenhang die Einstellungen über die Debugfähigkeit auf diesen Geräten oder die zu verwendene Android Version. Ein Bild zu der Oberfläche befindet sich unten. Die Manifestdatei legt auch noch fest, wie die einzelnen Komponenten miteinander kommunizieren können. Dafür sind Intents und intent-filter verantwortlich. Sie kümmern sich darum, ob eine Activity explizit oder implizit aufgerufen werden muss und regeln die Weiterleitung. Ihre Verwendung wird im Kapitel 3.4.6 Kommunikation durch Intents und Intent-Filter erläutert. 5 Entwicklung 6 Abbildung 2: Der Editor der Manifestdatei 3.3.2 Die Activity Das „Hello Android“ Projekt beinhaltet eine Activity als Haupteinstiegspunkt in das Programm. Activities sind zentrale Bausteine mit denen der User interagieren kann. Sie haben eine grafische Oberfläche und stellen eine Handlung dar, die der Nutzer ausführen kann. In dem Beispielprogramm wird ein Layout geladen, in welchem ein TextView enthalten ist, der den Text „Hello World, HelloActivity“ enthält. 3.3.3 Projektaufbau Das Projekt enthält einen Ordner src in dem der Quellcode der Applikation in Packages abgelegt ist. Der Ordner gen enthält ein Package mit demselben Namen wie dasjenige src, welches eine einzige Klasse mit dem Namen R.java enthält. Auf diese Klasse wird nach diesem Abschnitt genauer eingegangen. Des Weiteren enthält der Projektordner den Funktionsbaum der benutzten Android API Version, die Manifestdatei und einen Ordner res. In diesem Ordner befinden sich die Beschreibungsdateien von Oberflächen, Menüs und Beschreibungen in XML Format, sowie Icons für die logische Bebilderung der App (z.B. ein Zahnrad für einen 6 Entwicklung 7 Bearbeitungsknopf). Aus den XML Dateien werden die Oberflächen und Menüs zusammengesetzt. Die einzelnen Bestandteile können über die Klasse R.java angesteuert werden. 3.3.4 Die Klasse R.java In dieser Klasse sind alle Controls und Layouts als public static final int aufgelistet. Diese Klasse ist wiederum in public unter Klassen aufgeteilt. Für jede Kategorie, wie etwa attr, id, drawable, layout, string, gibt es eine statische Unterklasse. In der Unterklasse Layout sind alle Layout Dateien, wie zunächst die main.xml, als Integer repräsentiert. In id werden sämtliche Controls, die in allen Layouts definiert wurden, aufgelistet. Die Klasse R.java dient also dazu jedem Element eine ID zuzuordnen, um sie als Instanz programmatisch nutzbar zu machen. Die Controls eines in XML definierten Layouts sind allerdings erst nach der Methode setContentView(int), wobei der Parameter mit R.layout.my_layout gefüllt wird, ansteuerbar (my_layout.xml ist eine XML Layout Datei). 3.4 Komponenten Im folgenden Kapitel werden die grundlegenden Komponenten, welche in Android Applikationen verwendet werden, beschrieben. 3.4.1 Activities Activities stellen eine in sich geschlossene Komponente dar. Beispiele hierfür sind das Anlegen eines Kontakts oder die Auswahl von Fotos. Activities werden auf dem Activity Stapel, dem sogenannten Back Stack, abegelegt. Durch diesen Stapel verfügt jede Anwendung, die aus verschiedenen Activities, also aus mehreren Modulen besteht, bereits über eine einfache Navigationsmöglichkeit über den Zurückknopf, welchen jedes Android Gerät zur Verfügung stellt. Activities werden in der Manifestdatei entweder über den grafischen Assistenten oder direkt über XML eingetragen. Der Assistent bietet für die Erstellung von Activities auch die Möglichkeit Intent-Filter an die eigenen Activities zu binden. Mehr über Intent-Filter wird in Kapitel 3.4.2 erläutert. 7 Entwicklung 3.4.1.1 8 Lebenszyklus Jede Activity durchläuft einen Lebenszyklus von ihrer Erstellung bis zu ihrer Zerstörung. An diese Ereignisse bindet die Plattform Event Listener, mit denen ein Zugriff auf den jeweiligen Zustand möglich ist. Die Methoden für den Zugriff sind in der nachfolgenden Grafik aufgeführt. Abbildung 3: Lebenszyklus einer Activity nach [Activity 2011] An dieser Grafik kann man den Zyklus gut nachvollziehen. Auf onCreate(Bundle) folgt onStart() dann onResume() und onPause(), gefolgt von onStop() oder wieder von onResume(). Von onStop() aus wird die Activity entweder neu gestartet (onRestart()) oder zerstört (onDestroy()), welches den Zyklus verlässt. Aktionen wie z.B. eine Drehung des Geräts vom Landschafts- in den Portraitmodus startet die Activity erneut, d.h. onDestroy() und onCreate(Bundle) wird durchlaufen. Das ist notwendig, da man über verschiedene layout Ordner im Projektbaum unter res alternative Layouts für verschiedene Ansichten setzen kann. Mehr dazu wird in Kapitel 3.5 beschrieben. 8 Entwicklung Um 9 den zweiten Start zu beschleunigen kann man die Methode onSaveInstanceState(Bundle) überschreiben, um so z.B. den Status von Oberflächen zu speichern (dafür ist ein Aufruf der Originalmethode mit super.onSaveInstanceState(Bundle) notwendig) oder andere Daten in das Bundle zu schreiben. Bundle ist ein Assoziativer Speicher, welcher Key/Value Paare abspeichert. Als Key wird hier immer ein String verwendet. In der Methode onCreate(Bundle) wird dieses eventuell vorher beschriebene Bundle übergeben und kann so für einen möglicherweise zweiten Start der Applikation ausgelesen werden. Es ist hier unbedingt notwendig das Bundle auf null zu prüfen um eine Ausnahme zu verhindern. Zur Persistierung von wichtigen Daten wird angeraten Datenbankeinträge in onPause() zu schreiben, da nicht garantiert wird, dass onSaveInstanceState(Bundle) aufgerufen wird. 3.4.2 Kommunikation durch Intents und Intent-Filter Mit Intents wird dem Entwickler die Möglichkeit gegeben, dass sich Activities untereinander aufrufen können. Man unterscheidet zwischen expliziten und impliziten Intents. Eine Activity kann nur mit einem expliziten Intent gestartet werden wenn man für sie in der Manifestdatei keine Intent-Filter gesetzt hat. Intent-Filter sind also für den impliziten Aufruf einer Activity nötig. Sobald man ein Android Projekt erstellt wird eine Activity erzeugt, welche beim Start der Applikation aufgerufen wird. In der Manifestdatei des Projekts sind für diese Activity die folgenden Eintragungen erzeugt worden: <activity android:label="@string/app_name" android:name=".ImplicitIntentsActivity" > <intent-filter > <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> 9 Entwicklung 10 Im Intent-Filter der Activity sind die Tags action und category gesetzt worden. Action beschreibt auf welche Intents die Activity reagieren soll. Die main Action beschreibt das diese Activity der Haupteinstiegspunkt des Programms ist und auf den Activity Launcher reagiert. In Category ist der Launcher angegeben. Dies sorgt dafür, dass in dem Auswahlbildschirm der Apps des Geräts das Icon der Activity erscheint und so den Start der App anstoßen kann. Es gibt noch eine dritte Komponente die im Intent-Filter angegeben werden kann, nämlich das data Tag. Man kann Intents sogenannte Uris (Uniform Resource Identifier) mit auf den Weg geben, dafür muss allerdings noch ein Schema in dem data Tag definiert werden. Ein Beispiel könnte so aussehen: <activity android:name="ImplicitActivity"> <intent-filter> <action android:name="android.intent.action.VIEW"/> <action android:name="android.intent.action.EDIT"/> <action android:name="android.intent.action.PICK" /> <category android:name="android.intent.category.DEFAULT" /> <data android:path="de/android" android:scheme="content" /> </intent-filter> </activity> In der Activity ImplicitIntentsActivity könnte man jetzt ein URI Objekt definieren, welches per impliziten Intent an ImplicitActivity verschickt werden kann. m_Data = "content://de.android"; Uri uri = Uri.parse(m_Data); Intent impIntent = new Intent(Intent.ACTION_PICK, uri); startActivity(impIntent); In ImplicitActivity kann man die Uri, wenn sie den im Manifest definierten Konventionen entspricht, auslesen. 10 Entwicklung 11 public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent intent = getIntent(); Uri uri = Uri.parse(intent.getDataString()); LinearLayout l = new LinearLayout(this); TextView t = new TextView(this); t.setText(uri.toString()); l.addView(t); setContentView(l); } Mit diesen Vorgehensweisen kann man in seiner Anwendung auch die Activities von anderen Apps nutzen. Auf die action View der Android Plattform regieren viele Activities. Wenn die eigene Activity auch Intents mit der action View verarbeiten kann, erscheint, sobald man folgenden Intent Aufruf macht, eine Liste mit allen Activities, die auf diesen Intent reagiert, also auch die eigene. Intent intent = new Intent(Intent.ACTION_VIEW); startActivity(intent); So ist es z.B. möglich, mit seiner App Anrufe zu tätigen oder eine Nummer mit dem Dialer zu wählen. Man muss nur die entsprechenden Intents verarbeiten und aussenden. Die Klasse Intent definiert viele Konstanten, die auf Activities der Plattform verweisen. Sie beginnen immer mit dem Präfix ACTION_. In die Manifestdatei werden die actions ohne dieses Präfix eingetragen. 3.4.3 Broadcast Receiver Broadcast Receiver sind Komponenten die statisch oder dynamisch auf Broadcast Intents horchen und beim Empfangen eines solchen impliziten Intents ihre Aufgabe ausführen. Broadcast Intents sind implizite Intents. welche auf einer anderen Schicht als normale Intents verschickt werden. Activities können also nicht auf Broadcast Intents horchen. Außerdem ist es 11 Entwicklung 12 nicht möglich, dass Broadcast Receiver explizite Intents nutzen. So müssen alle Activities, die von einem Broadcast Receiver möglicherweise ausgelöst werden wollen, in ihren Intent Filtern die action gesetzt haben, die der Broadcast Receiver losschickt. In folgender Abbildung sieht man eine Auflistung von verschiendenen Broadcast Intents und deren Bedeutung. Abbildung 4: 9.1 Broadcast Intents [Becker, S.142, Tab. 9-1] Eine statische Verwendung eines Broadcast Receivers bedeutet, dass dieser in der Manifestdatei ähnlich wie eine Activity verankert wird. Dort wird festgelegt, auf welchen Broadcast Intent er reagieren soll. Die programmatische Manifestation eines solchen Receivers findet in einer eigenen Klasse statt, die von BroadcastReceiver ableitet. Dort muss dann die Methode onReceive() implementiert werden, welche festlegt was passieren soll, wenn ein registrierter Intent eingeht. Als Beispiel könnte man eine Activity starten oder eine kleine Systemnachricht wie einen Toast senden. Die dynamische Verwendung von Broadcast Receivern bedeutet, dass man ihn in einer Klasse innerhalb der Activity Klasse definiert und benutzt. Activity ist von Context abgeleitet, welches die Methode registerReceiver(BroadcastReceiver, intentFilter) zur Verfügung stellt. Dort kann also ein BroadcastReceiver mit Intent Filter, der ihn auslösen soll, registriert werden. Dies geschieht alles innerhalb der Activity Klasse und erfordert keine Registrierung in der XML Datei. 12 Entwicklung 13 3.4.4 Fragments Fragments sind seit der API Version 11 in die Android Plattform integriert. Sie verhalten sich zunächst wie gewöhnliche Steuerelemente, dienen aber dem Zweck vor allem auf Tablets, die Trennung zwischen Übersicht und Detailansicht, wie sie z.B. auf Smartphones benötigt wird aufzuheben. Auf Tablets ist genug Platz für die Möglichkeit jederzeit navigieren zu können. Beispielsweise mit einer Menüleiste, die permanent am linken Rand des Geräts sichtbar ist. Tippt man einen Menüpunkt an wird der freie Bereich, der zu der Activitiy gehört, die auch das Menü beinhaltet, mit Fragments gefüllt. Diese Fragments werden an die Activity gebunden und haben von nun an ihren eigenen Lebenszyklus, der eng mit der Activity verzahnt ist. Auch hier gibt es wie bei Activities Methoden, mit denen man sich an die einzelnen Zustandsevents onCreate() ム onDestroy() anhängen kann. Zusätzlich kommen hier noch die Zustände für das Anhängen des Fragments an eine bestimmte Activity (onAttach()), optional das Entfalten ihrer Oberfläche (View onCreateView(LayoutInflater, ViewGroup, Bundle)) und das Abkoppeln des Fragments von der Activity (onDetach()) hinzu. Sonst verhält sich der Lebenszyklus ähnlich zu dem der Activity und richtet sich auch nach ihr. Wird onStop() in der Activity aufgerufen wird die äquivalente Methode im Fragment aufgerufen. Ein Fragment muss nicht unbedingt eine Oberfläche entfalten, es kann auch nur für Berechnungen genutzt werden. Ein Fragment wird in einer Activity über das Tag <fragment/> in der XML Datei der Activityoberfläche hinzugefügt. Ein Fragment lässt sich auch programmatisch über folgende Befehlskette einer Activity hinzufügen: Fragment newFragment = new MyFragment(); FragmentTransaction ft = getFragmentManager().beginTransaction(); ft.add(CONTENT_VIEW_ID, newFragment).commit(); Das Fragment wird in einer Klasse, welche von Fragment ableitet, manifestiert. Hierbei ist darauf zu achten, falls man ein selbst definiertes Layout in seinem Fragment entfalten möchte, die Methode inflate(int id, ViewGroup root, boolean attachToRoot) mit attachToRoot = false aufzurufen. Andernfalls wird eine Ausnahme geworfen, welche besagt, dass das spezifizierte Kind bereits ein Elternelement hat [Crafty 2011]. Jede Activity seit Android 3.0 bietet die Methode getFragmentManager() an, über die die Fragment Transaktionen getätigt werden. Über diese Transaktionen kann auch mit der „Zurück“ 13 Entwicklung 14 Schaltfläche der Android Plattform gearbeitet werden. Dies bietet sich an, da man verschiedene Funktionalitäten in einer Activity darstellt. Um zwischen ihnen hin und herzugelangen sollte man, um dem User eine gewohnte Benutzung zur Verfügung zu stellen, mit dem Back Stack Prinzip der Activities arbeiten. 3.4.5 Services Services sind Komponenten, die keine Benutzeroberfläche zur Verfügung stellen. Sie unterscheiden sich von Broadcast Receivern in so fern, dass sie nicht nur bei einem Ereignis aktiviert werden und eine längere Laufzeit, sogar über die der Anwendung die sie gestartet hat hinaus, haben. Services können nach Abschluss ihrer Operation programmatisch oder auf dem Endgerät unter Einstellungen • Anwendungen • Aktive Dienste beendet werden. Sie werden wie alle bisher vorgestellten Bausteine in der Manifestdatei unter dem Tag <service/> deklariert. Ihr Namensattribut verweist auf eine Klasse, in der der Service implementiert ist. Dies ist normalerweise eine von android.app.Service abgeleitete Klasse. Services haben einen Lebenszyklus, der mit den folgenden Methoden dargestellt wird: onCreate(), onStartCommand(Intent, int, int), onBind(Intent), onUnbind(Intent), onRebind(Intent), onDestroy(). Services sind keine Threads. Sie arbeiten als Standard auf dem Mainthread. Man kann ihnen aber andere Threads, wie z.B. den Remotethread, zuweisen, um sie zum Arbeiten auf der Betriebssystemebene zu zwingen und sie zur Kommunikation zwischen mehreren Bausteinen oder Prozessen zu nutzen. Dazu kann man im Tag des Services in der Manifestdatei das Attribut android process Remote setzen, dann nennen sie sich Remotethreads. Da sie normalerweise auf dem Mainthread arbeiten, sollte man komplexere Operationen in einem eigenen Thread ausführen um ANR (Application not responding) Fehler zu vermeiden. Man unterscheidet zwei Arten von Services Remoteservices und Localservices. Im Folgenden wird genauer auf Localservices und ihren Aufbau eingegangen. Jeder Service muss die Methode onBind(Intent) überschreiben. Diese Methode liefert ein Objekt vom Typ IBinder, welches sich um den Zugriff auf das Serviceobjekt kümmert. Eine Möglichkeit für diesen Zugriff ist die Implementierung seines Binders als innere Klasse des Services durchzuführen. So kann man über diesen Binder Zugriffsmethoden für die Membervariablen implementieren. In der zugehörigen Activity wird eine Variable vom Typ ServiceConnection benötigt. Diese ist ein Interface und zwingt den Nutzer, die Methoden 14 Entwicklung 15 private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName name, IBinder service) { LocalBinder binder = (LocalBinder) service; mService = binder.getService(); } public void onServiceDisconnected(ComponentName name) { mService = null; } }; zu implementieren. Der LocalBinder wurde zuvor im Service definiert und hat nun Zugriff auf seine Membervariablen. Alternativ kann man sich für die Verwendung eines Services in der Aktivity auch eine Instanz von dem zu verwendenen Service über den Binder holen, indem man die Methode getService() wie oben beschrieben aufruft und den zurückgegebenen Wert speichert. In der Methode onStart() der Activity wird der Service an die Activity gebunden und in onStop() wieder gelöst. Die Registrierung des Services erfolgt über einen expliziten Intent. Die Methode bindService(Intent, ServiceConnection, int) prüft durch den Schlüssel BIND_AUTO_CREATE, ob der Service schon läuft und startet ihn gegebenenfalls. @Override protected void onStart() { super.onStart(); Intent intent = new Intent(this, LocalService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onStop() { super.onStop(); if (mService != null) { unbindService(mConnection); mService = null; } } 15 Entwicklung 16 Der explizite Intent zum Starten des Services ist ein wesentlicher Unterschied vom lokalen Service zum Remote Service. Remote Services sollen die Kommunikation zwischen Prozessen regeln. Lokale Services existieren solange wie Activities oder andere Komponenten, die sich bei ihnen registriert haben, arbeiten. Sobald die letzte Komponente die den Service nutzt zerstört wird, wird der Service ebenfalls zerstört. Remote Services unterscheiden sich von Local Services dadurch, dass sie auf einem anderen Thread arbeiten, die Kommunikation mit dem Service über per AIDL (Android Interface Definition Language) erstellte Interfaces abläuft und man sowohl Messages als auch Handler für die Kommunikation zwischen den Prozessen (UI und Remote Thread, IPC (Inter Process Communication) nutzt. Um den Service auf dem Remote Thread auszuführen setzt man in der Manifestdatei in dem Tag des Services das Attribut android process remote . Zunächst werden Interfaces per AIDL definiert. Dazu erstellt man mit eclipse eine neue Datei vom Typ File (File • new • File) mit der Endung .aidl. In diese Datei schreibt man die Definition seines Interfaces in Java. package com.callback; import com.callback.IRemoteServiceCallback; interface IRemoteService { void registerCallback(IRemoteServiceCallback cb); void unregisterCallback(IRemoteServiceCallback cb); } In diesem Beispiel werden die Methoden zur Registrierung eines Service durch die Verarbeitung eines anderen Interfaces bereitgestellt. Das zweite Interface soll sich um die Aktualisierung des UI Threads, also der Oberfläche kümmern. Die Verbindung zu dem Service läuft wie gewohnt über ein Objekt, welches als Member der Activity Klasse gespeichert wird, in onServiceConnected(ComponentName className, IBinder service) ab. mConnection = new ServiceConnection() 16 Entwicklung 17 { public void onServiceConnected(ComponentName className, IBinder service) { mService = IRemoteService.Stub.asInterface(service); mCallbackText.setText("Attached."); try { mService.registerCallback(mCallback); } catch (RemoteException e) { } } public void onServiceDisconnected(ComponentName className) { } } Callback ist eine Instanz einer Klasse von IRemoteServiceCallback, welche nur die Methode void valueChanged(int value) implementiert. In dieser Methode findet die Kommunikation zwischen den Prozessen statt. Im Remote Service selbst existiert eine Liste von allen Activities, die bei dem Service registriert wurden, welche aus diesen Callback Instanzen besteht. So können, sobald sich Werte ändern, über Handler und Messages alle registrierten Activities mit diesen Werten aktualisieren. 3.5 Resourcen und ihre Verwaltung Android bietet eine einfache Möglichkeit um auf verschiedene Sprachen oder auf verschiedene Ausrichtungen des Geräts zu reagieren. Der Ordner res definiert alle Resourcen, die das Projekt benötigt. Es gibt dort einen Ordner values und einen Ordner layout. Standardmäßig sucht Android in den vorinstallierten eben genannten Ordnern nach den XML Dateien die es verwenden möchte. Erstellt man nun einen Ordner layout-land, erkennt Android bei einer Drehung des Geräts automatisch, dass er die Layout XML Datei nun in diesem (Landscape) Ordner zu suchen hat und bindet diese, in setContentView(int) anstatt der anderen, für den Portrait Modus ein. Das Gleiche gilt für die Sprache einer Anwendung. Wenn man einen 17 Entwicklung 18 Ordner values-en erstellt, wird Android bei einer Änderung der Sprache in diesem Ordner nach einer strings.xml Datei suchen. Dies richtet sich danach, welche Sprache auf einem Gerät eingestellt wurde. Findet Android keine passende Sprache für die App nimmt er diejenige, die in dem values Ordner ohne Zusätze enthalten sind. 3.6 Views und Viewgroups Die Klasse View ist die Basisklasse für alle Steuerelemente, die man auf der Oberfläche sehen kann. Sie definiert einen rechteckigen Bereich an einer Position. Außerdem gibt es Einstellungen für den Abstand zu den Seiten (Padding) und für die Höhe und Breite. Für Höhe und Breite definiert View zahlreiche Konstanten, die auch in der XML Layout Datei gesetzt werden können. Die Konstante wrap_content rendert den View so, dass z.B. ein Button genauso groß dargestellt wird wie seine Beschriftung Platz benötigt. Jeder View kümmert sich um sein rendering selbst; es kann durch die Methode onDraw() manipuliert werden. Viewgroup ist eine Unterklasse von View. Sie stellt einen Container dar und bietet die Möglichkeit Views zu gruppieren. Dafür stellt sie die Methode addView(View) zur Verfügung. In der Regel benutzt man abgeleitete Klassen von Viewgroup, wie z.B. das FrameLayout, welches nur ein direktes Kind haben darf, oder das LinearLayout, welches seine Kinder horizontal oder vertikal anordnen kann. Die Oberfläche wird in einer Layout XML Datei zusammengesetzt. Mit der Methode setContent(int) kann man über die oben erwähnte Klasse R.java die Oberfläche der Activity setzen und diese auch dynamisch erweitern, indem man programmatisch neue Views erzeugt und den bestehenden Viewgroups mit addView(View) hinzufügt. Die Attribute der Views und Viewgroups sind über die XML Datei voll steuerbar. Durch das Attribut android: innerhalb des Tags des Views, bekommt man Zugriff auf diese Attribute. Die Plattform stellt dem Entwickler den Zugriff auf die Ausrichtung eines Views, seine Hintergrundfarbe oder vieles mehr zur Verfügung. Die Aufrufe hierfür lauten in der XML Datei android:background="@android:color/holo_blue_dark" android:layout_gravity="center_vertical". 18 Entwicklung 3.7 19 Multitasking Android unterstützt präemptives Multitasking. Nach Magreans IT-Systeme Skript beschreibt präemptives Multitasking eine Verdrängungsstrategie, in der das Betriebssystem entscheidet, wann ein Prozess zur Ausführung kommt und wann zwischen ihnen gewechselt wird [Magrean 2010]. Der programmatische Zugriff auf die Auslagerung von Operationen in Prozesse wird mit den Java Threads der Java Klasse java.lang.Thread umgesetzt. Dafür ist die Implementierung eines Runnable Interfaces in einer Klasse oder einer anonymen Klasse nötig. Diese implementiert dann die Methode run und kann an einen Thread übergeben werden. In der Runnable Instanz wird definiert, welche Operation ausgeführt werden soll. Der Thread verwaltet die Operation und bietet Möglichkeiten die Operation zu starten, zu stoppen, zu prüfen ob die Operation abgeschlossen wurde, die Operation zu pausieren und vieles mehr. Eine wichtige Operation stellt die Abgabe von Rechenzeit an andere Threads mit dem statischen Aufruf von Thread.yield() dar, um die zur Verfügung stehenden Ressourcen optimal zu verteilen. Ein weiterer wichtiger Punkt ist die Steuerung der Threads von außen, indem man z.B. eine Abbruchbedingung in der Runnable Instanz definiert. Dies kann über eine while Schleife erfolgen, die auf eine Membervariable prüft. Hier kommt das Schlüsselwort volatile ins Spiel. Volatile bedeutet, dass die so gekennzeichnet Variable von allen Threads gesehen und verändert werden kann. Sie wird hinter dem Zugriffsmodifizierer platziert. Dadurch kann ein Thread bei seinem Abschluss diese Variable verändern und ggf. verhindern, dass ein anderer Thread fertiggestellt wird, der diese Variable in seiner Runnable Instanz prüft. Bei der Verwendung von Threads gelten die Regeln zur Verhinderung von unbeabsichtigten race conditions oder dead locks. Um den Zugriff von Threads auf Methoden eines Objekts zu steuern bietet Java das Schlüsselwort synchronized an. Methoden, die hiermit gekennzeichnet werden, können nach Ullenboom nur von einem Thread gleichzeitig genutzt werden. Ein zweiter Thread muss solange warten bis die Ressource von dem anderen wieder freigegeben wurde [Ullenboom 2010]. Alle Apps, die auf der Android Plattform ausgeführt werden, laufen zunächst im Main Thread oder UI Thread. Wenn sich dieser Thread nach fünf Sekunden nicht gemeldet hat bekommt der Nutzer eine ANR (Application not Responding) Meldung. Um dies zu vermeiden muss man längere Operationen in andere Threads auslagern. Wenn diese Operationen nun die Oberfläche 19 Entwicklung 20 auch noch aktualisieren sollen, sollten AsyncTasks verwendet werden. AsyncTask ist eine abstrakte Android Klasse, von welcher die Methode doInBackground() überschrieben werden muss. Diese Methode läuft in den Hintergrund-Threads und führt die Operationen aus, welche die Oberfläche ändern. Die Methode onPostExecute() verarbeitet die Ergebnisse von doInBackground() und teilt sie dem UI Thread mit. Der Benutzer kann mit der Methode onProgressUpdate() von dem Fortschreiten eines Prozesses unterrichtet werden, z.B. mit einer Ladeanimation. Ausführliches Code Beispiel mit Bildern in Anhang. 3.8 SQLite Zur persistenten Datenspeicherung wurde in die Android Plattform SQLite integriert. Nach Künneth ist es eine in sich geschlossene, serverlose und ohne Konfigurationsaufwand nutzbare transaktionale SQL-Datenbankmaschine [Künneth 2011]. Die Einbettung von SQLite in Android bietet programmatischen Zugriff auf die Erstellung, das Aktualisieren, das Löschen und Einfügen von Daten in ein relationales Schema. 3.8.1 Arbeiten mit der Kommandozeile In SQLite entspricht eine Datenbank immer einer Datei auf dem Gerät, die standardmäßig unter /data/data/package_name/databases abgelegt wird. Wenn man die Umgebungsvariable seines Systems um die Einträge der vollständigen Pfade von plattform-tools und tools des Android Installationsverzeichnis erweitert, kann man über die Kommandozeile oder Shell auf SQLite und andere Features des Android SDKs zugreifen. Dies ist ein wertvolles Mittel, um seine Datenstruktur während der Entwicklung im Blick zu behalten und ggf. einzulenken. Wie man eine SQLite Datenbank in der Eingabeaufforderung erstellt und darauf arbeitet, wie man sich auf die programmatisch von seiner App im Emulator erstellten Datenbanken zugreift und wie man auf einem echten Gerät an die Datenbanken kommt, wird im Folgenden für einen Windows Rechner vorgestellt. Zunächst öffnet man die Konsole mittels Eingabe von cmd in das Suchen Fenster unter Windows. Dort tippt man, wenn die Umgebungsvariable path des Betriebssystems um die Android Verzeichnisse plattform-tools und tools erweitert wurden, adb shell ein. ADB steht für Android Debug Bridge. Dort liegen unter /data/data alle Pakete der Beispiel- und die der selbst 20 Entwicklung 21 erstellten Applikationen. Das ist später wichtig, um auf die Datenbanken seiner eigenen Apps zuzugreifen. Als nächstes kann man mit dem Befehl sqlite3 SQLite öffnen und bekommt Informationen wie die aktuelle SQLite Versionsnummer und die Hilfe angezeigt. Danach kann man sqlite3 /pathXYZ/name.db eingeben. Also irgendein Pfad, wo die Datenbank erzeugt werden soll oder wohin man eine Verbindung aufbauen möchte und den Namen der Datenbank, die man erstellen oder öffnen möchte. Dann kann man SQL Befehle zum Erstellen und Hinzufügen von Daten eintippen und sich die Daten ebenfalls mit SQL ansehen. SQL Befehle sollen an dieser Stelle nicht erläutert werden, im Folgenden wird auf ADB eingegangen. Sobald man adb shell eingegeben hat, befindet man sich in einer Linux Applikation. Dort gelten dann also Linux Befehle. Mit ls zum Beispiel kann man sich ansehen welche Dateien sich in einem Ordner befinden. Mit dem Befehl adb pull kann man eine Datei aus dem Linux Dateisystem an eine Zielort kopieren. Dort kann man dann z.B. mit der Datenbank in einem externen Plugin, wie etwa SQLiteManager für Firefox, arbeiten. Zur Ausführung des Systems schreibt man in der Shell adb pull /Pfad in dem Linuxsystem C:\Pfad auf seinem Rechner. 3.8.2 Datenbankzugriff in der App Für den programmatischen Zugriff auf eine SQLite Datenbank stellt die Android Plattform zwei zentrale Hilfsklassen zur Verfügung. Den SQLiteOpenHelper, der die Datenbankverbindung aufbaut und les- oder schreibbare Instanzen von SQLiteDatabase, der zweiten Klasse, die die Anfragen und Datenbankoperationen verwaltet, zur Verfügung stellt. Der Weg zum programmatischen Zugriff auf eine Datenbank erfolgt in der Regel über eine von SQLiteOpenHelper abgeleitete Klasse, die als Manager für die Datenbank fungiert. SQLiteOpenHelper ist eine abstrakte Klasse. Jede davon abgeleitete Klasse muss die Methoden onCreate() und onUpgrade() implementieren. Der SQLiteOpenHelper benötigt für den Aufbau einer Datenbankverbindung den Context der Applikation, einen Namen für die Datenbank, also der Datei die erstellt werden soll oder zu der verbunden werden soll, und eine Versionsnummer vom Typ Integer des aktuellen Schemas. In der Methode onCreate() bietet es sich je nach Situation an, die Tabellen mit denen gearbeitet werden soll, zu erstellen. Dafür muss man sich mit dem this Objekt eine schreibbare Instanz der Datenbank holen. Dies geschieht mit der Methode getWritableDatabase(). Dann kann man mit execSQL(String sql) ein Statement schreiben, welches die Tabelle erzeugt, wie z.B. CREATE TABLE xyz column INTEGER . Man kann mit 21 Entwicklung 22 execSQL(String sql) jedes beliebige Statement ausführen. Statements zum Erstellen einer Tabelle bieten sich allerdings an, da die Methode keinen Rückgabewert hat. Android verkapselt jeden Typ von SQL Statement in Methoden, für die man nur sehr wenig SQL Kenntniss haben muss. In der folgenden Tabelle stehen die veschiedenen Methoden mit Beschreibung ihrer Funktion. Abbildung 1: 11.5.1 SQLiteDatabase – Verbindung zur Datenbank [Becker 2009, S.167, Tab. 11-1] 3.8.3 Cursor Die Methoden query und rawQuery geben so genannte Cursor zurück. Ein Cursor ist ein Objekt, welches sich genau an einem Datensatz positioniert und so die Daten ausliest. Der Cursor kann auch als Iterator genutzt werden und mit der Methode moveToNext() in die nächste Reihe der Tabelle geschoben werden, um den Datensatz dort zu lesen. 3.9 Content Provider Content Provider stellen eine Schicht zwischen dem Datenzugriff und der Manifestation von zu verarbeitenden Daten dar. Sie sind sehr leicht austauschbar und können den Zugriff auf viele Formate wie z.B. RSS Feeds, Webserver oder SQLite Datenbanken verwalten. Content Provider von fremden Applikationen lassen sich durch das Setzen von Berechtigungen in der Manifestdatei der eigenen Applikation (Tag <uses-permission android:name="YOUR_CONTENT_PROVIDER">) verwenden. Im Folgenden wird auf die Verwendung von Content Providern in SQLite Datenbanken eingegangen. 22 Entwicklung 23 Im vorherigen Kapitel wurde detailliert auf die Verwendung von SQLite Datenbanken eingegangen. Bisher wurde ein Datenbankzugriff über eine Instanz der Klasse SQLiteOpenHelper, deren Methoden wie z.B. execSQL(String sql) dann in der Activity verwendet wurde. Nun schalten wir eine Schicht zwischen diesen Zugriff. Dadurch, dass Activity von Context abgeleitet ist, hat man dort Zugriff auf die Methode getContentResolver(), welche eine Instanz von ContentResolver zurückgibt. Mit diesem Objekt kann man nun Methoden für Lese-, Schreib-, Lösch- und Einfügeoperationen aufrufen. Diese wiederum rufen im Content Provider verkapselte Datenbankoperationen auf. Die Membervariable für den Datenbankmanager wird in der onCreate() Methode des Providers gesetzt und im weiteren Programmfluss in den überschriebenen Methoden von query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder), update(Uri uri, ContentValues values, String selection, String[] selectionArgs), delete(Uri uri, String selection, String[] selectionArgs) und insert(Uri uri, ContentValues values) benutzt. Zu diesem Zeitpunkt ist noch offen, von welchem Content Provider die Methoden genutzt werden. Um den Content Provider zu identifizieren bekommt jede Methode ein Uri Objekt übergeben, welches zu dem Uri Objekt des Content Providers passen muss. Im Content Provider selbst kann über diese Uri festgemacht werden, auf welche Tabellen in der Datenbank zugegriffen werden soll. Es ist möglich an Hand der Uri so zu verzweigen, dass nur ein Eintrag mit bestimmter ID oder alle Einträge in der Datenbank eingetragen werden. Dazu gibt es den UriMatcher, der durch vorher registrierte Konstanten unterscheiden kann, was der Nutzer mit der Uri tun möchte. 23 Techniken 4. Techniken 4.1 Debugging 24 4.1.1 LogCat Das LogCat bietet eine Möglichkeit, sich Zusammenhänge im Programmfluss, in einer zusätzlichen Sicht ähnlich zur Java Konsole, ausgeben zu lassen. Dafür kann man einfach im Quellcode Log.d(String tag, String value), .e, .i, .v oder .w schreiben und sich an Hand des eingegebenden Tags einen Wert ausgeben lassen, der sich vielleicht aus dem Programmkontext zusammensetzt oder einfach eine selbst geschriebene Notiz ausgibt. Die Kleinbuchstaben d, e, i, v und w stehen für debug, error, information, verbose und warning. Sie stellen die verschiedenen Sichten von LogCat dar, in der die unterschiedlichen Operationen protokoliert werden. Mit den entsprechenden Buchstaben kann man sich also Ausgaben in der Fehlersicht oder der Warnungssicht ausgeben lassen. Ein Einsatzgebiet wäre z.B. einen Log Eintrag am Anfang einer Methode zu setzen um zu sehen, ob sie überhaupt durchlaufen wird oder sich den aktuellen Eintrag eines Arrays anzusehen. 4.2 Auslesen von Höhe und Breite eines Layouts Android setzt die tatsächlichen Größen eines Layouts erst nach der Initialisierung des Programmkontexts. Damit ist der Programmfluss nach dem Aufruf der Methode onResume() einer Activity gemeint. Zu diesem Zeitpunkt sollte die Oberfläche, welche aus möglicherweise entfalteten XML Bäumen und dynamischen Komponenten besteht, für die Benutzung fertig sein. Hat man nun den Fall, dass man die Oberfläche an Hand der Breite eines Layouts anpassen muss, um z.B. die maximale Anzahl an Buttons einer festen größe zu bestimmen die in das Layout einer Zeile hineinpassen ohne abgeschnitten zu werden, ist dies im gängigen Initialisierungskontext nicht möglich. Um dieses Problem zu lösen kann man sich des folgenden Listeners bedienen, welcher in jeder Klasse, die von View ableitet, integriert ist: m_ButtonContainer.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { public void onGlobalLayout() { if(m_bTellWidthToCategory) { 24 Techniken 25 m_Category.fillRow(row, m_ButtonContainer.getWidth()); m_bTellWidthToCategory = false; } } }); Die Variable m_ButtonContainer ist vom Typ LinearLayout. Der Listener wird zum ersten Mal ausgelöst wenn die Initialisierungen abgeschlossen sind und das Layout gezeichnet wird. Diese Methode wird sehr oft aufgerufen. Man sollte also darauf achten eine Abfrage zu machen, damit nicht jedes Mal der gesamte Methodenrumpf ausgeführt wird. Das ist notwendig da diese Methode jedesmal ausgeführt wird wenn sich etwas auf der Oberfläche ändert, wie z.B. wenn ein Button betätigt wird. Wenn die Methode es zulässt wird eine Methode ausgeführt den Container (m_ButtonContainer) zu füllen solange er leer ist, also genau einmal. Breite und Höhe sind vor den Initialisierungen nämlich immer 0, egal ob man nun die LayoutParams mit festen Zahlen oder mit den Konstanten match_parent (das Layout soll in Breite oder Höhe, diejenige seines Eltern Objekts annehmen) setzt. 4.3 Drag and Drop Die Android API bietet Methoden für Drag and Drop an. In die Klasse ist die Methode startDrag(ClipData, DragShadowBuilder, View, int) integriert. Diese Methode sorgt dafür, dass an einer Stelle des Programmflusses eine Dragoperation starten kann. Gewöhnlicherweise erfolgt dies über einen langen Klick auf das View Objekt, also im Methodenrumpf der OnLongClick Methode des Interfaces, welches im entsprechenden View registriert wurde. Nach dem Starten des Drags kann man nun mit allen Objekten interagieren, die einen OnDragListener bei sich mit der Methode setOnDragListener(OnDragListener) registriert haben. Dieser ist auch wieder ein Interface und wird von View abgeleitet. Im OnDragListener muss der Drag in der Methode onDrag(View, DragEvent) schließlich verarbeitet werden. Mit DragEvent kann man den Status des Drags abfragen. In ihm sind Konstanten wie ACTION_DROP und ACTION_DRAG_ENTERED gespeichert, die man mit der Methode getAction() abfragen kann. public boolean onDrag(View self, DragEvent event) { if (event.getAction() == DragEvent.ACTION_DRAG_STARTED) { 25 Techniken 26 } else if (event.getAction() == DragEvent.ACTION_DRAG_ENTERED) { m_bInsideOfMe = true; Log.d("Test", "Entered"); self.setBackgroundDrawable(getResources().getDrawable(R.drawable.borde r_red)); } else if (event.getAction() == DragEvent.ACTION_DRAG_EXITED) { m_bInsideOfMe = false; self.setBackgroundDrawable(null); } else if (event.getAction() == DragEvent.ACTION_DROP) { if (m_bInsideOfMe) { ViewGroup container = (ViewGroup)self; container.addView(new TextControl(m_Context, false)); } self.setBackgroundDrawable(null); } else if (event.getAction() == DragEvent.ACTION_DRAG_ENDED) { self.setBackgroundDrawable(null); } return true; } Dieser OnDragListener bewirkt, dass ein farbiger Rahmen um die Viewgroup gelegt wird die ihn implementiert (self), sobald man ihn mit dem Objekt, welches im Drag ist, betritt und sobald man es verlässt wieder entfärbt. Lässt man es im Container los, wird ein neues Objekt vom Typ TextControl in den Container eingefügt. 26 Fazit und Ausblick 5. 27 Fazit und Ausblick In dieser Arbeit wurde die Vielfältigkeit von Android und seine Konzepte gängige Programmiermuster umzusetzen aufgezeigt. Android ist eine solide Plattform zur Entwicklung mobiler Applikationen. Durch die Programmiersprache Java ist eine einfache Verwendung gewährleistet, da sich um Zusatzarbeit, wie die dynamische Speicherallokierung durch die Garbage Collection, nicht gekümmert werden muss. Die Verwendung von Android als freie Plattform ist für kleinere Unternehmen sicherlich von Vorteil, da sie auf gängiger Hardware lauffähig ist. Rechner, die dem Industriestandard Windows entsprechen, reichen aus. Die Verwendung erfordert also keine große Umgewöhnung. Zusätzlich gibt es noch Versionen des SDKs und der ADT für Mac OS und Linux Systeme. Der Vorteil von freier Software und der Größe der Community, die für Android Applikationen entwickelt, kann allem anschein nach aber ebenso von Nachteil sein. Bei der Verwendung von SDK, eclipse und ADT-Plugin fielen Komformängel und Bugs auf, die mehr oder weniger reproduzierbar und von System zu System trotz ähnlicher bis gleicher Installationsweise auftraten. Beispiele hierfür sind das Starten einer entwickelten Applikation während eine XML Datei im Fokus ist. Das Resultat ist, dass das Programm nicht startet und eine gleichnamige XML Datei mit dem Zusatz .out erscheint, welche Fehler im Projekt erzeugt. Dieser Fehler trat auf zwei verschiedenen Rechnern auf und die Datei lässt sich erst nach einem Clear-Befehl des Projekts löschen. Ebenso ist die Darstellung einer Layout Datei in XML nicht ganz ausgereift, da bei vielen Änderungen ohne zu speichern, die XML Datei nicht korrekt dargestellt wird und die Zeilen zur Darstellung des vorher gespeicherten Dateizustandes passen und nicht zum aktuellen. Ein Speichern, Schließen und wieder Öffnen der Datei schafft Abhilfe, lässt aber Komfort bei der Arbeit vermissen. Alles in allem ist Android trotzdem zu empfehlen, da man sehr schnell Ergebnisse erzielen kann und die Masse an Endnutzern für Entwickler auch eine gewisse Sicherheit hinsichtlich Support, Zukunftssicherheit und Kunden gewähren sollte. 27 Literatur- und Quellenverzeichnis I Literatur- und Quellenverzeichnis [Künneth 2011] Künneth, T.: Android 3 Apps entwickeln mit dem Android SDK. 1. Aufl. Bonn: Galileo Press 2011. [Android 2011] Android Developers. What is Android. URL:http://developer.android.com/guide/basics/what-is-android.html. Accessed: 2011-1025. (Archived by WebCite® at http://www.webcitation.org/62h6RgQjv) [Kowitt 2011] Kowitt, Beth. 100 million Android fans can't be wrong. URL:http://tech.fortune.cnn.com/2011/06/16/100-million-android-fans-cant-be-wrong/. Accessed: 2011-12-13. (Archived by WebCite® at http://www.webcitation.org/63tgt0CLO) [Stanford 2011] Stanford Technology Ventures Program. Andy Rubin. URL:http://ecorner.stanford.edu/author/andy_rubin. Accessed: 2011-10-25. (Archived by WebCite® at http://www.webcitation.org/62hEp2Duu) [Murph 2011] Murph, Darren. Amazon Kindle Fire tablet unveiled: Android-based, 7-inch display, $199 price tag. URL:http://www.engadget.com/2011/09/28/amazon-fire-tabletunveiled-7-inch-display-199-price-tag/. Accessed: 2011-10-29. (Archived by WebCite® at http://www.webcitation.org/62nIPhZKS) [Becker 2009] Becker, A., Pant, M.: Android Grundlagen und Programmierung. 1.Aufl. Heidelberg: dpunkt.verlag GmbH 2009. [Magrean 2010] Magrean, Benedikt. IT-Systeme Teil 2: Betriebssysteme. URL:http://www.rz.rwth-aachen.de/global/show_document.asp?id=aaaaaaaaaacacpz. Accessed: 2011-11-12. (Archived by WebCite® at http://www.webcitation.org/638npW7Le) [Ullenboom 2010] Ullenboom, C.: Java ist auch eine Insel. Das umfassende Handbuch. 9. Aufl. Bonn: Galileo Press 2010. [Crafty 2011] Crafty. Android Fragment (with Compatibility Package on 2.3.3) creates “Specified child already has a parent error”. Literatur- und Quellenverzeichnis II URL:http://stackoverflow.com/questions/6035711/android-fragment-with-compatibilitypackage-on-2-3-3-creates-specified-child. Accessed: 2011-11-29. (Archived by WebCite® at http://www.webcitation.org/63YLABJ1c) [Activity 2011] Android Developers. Activity Lifecycle. URL:http://developer.android.com/reference/android/app/Activity.html. Accessed: 201111-28. (Archived by WebCite® at http://www.webcitation.org/63WsiZ7uZ) [NDK 2011] Android Developers. What is the NDK? URL:http://developer.android.com/sdk/ndk/overview.html. Accessed: 2011-12-13. (Archived by WebCite® at http://www.webcitation.org/63ton55rI) Anhang III Anhang NDK Das NDK (Native Development Kit) benutzt C/C++ Code für seine Anwendungen und läuft gelöst von Dalvik in seiner eigenen Laufzeitumgebung. Dies erhöht für bestimmte Anwendungen die Ausführungsgeschwindigkeit. Es kommt in der Simulation von physikalischen Zusammenhängen sowie in unabhängigen CPU intensiven Operationen zum Einsatz. Das NDK verarbeitet drei ARM CPU Befehlssätze: ARMv5TE (including Thumb-1 instructions) ARMv7-A (including Thumb-2 and VFPv3-D16 instructions, with optional support for NEON/VFPv3-D32 instructions) x86 instructions (see CPU-ARCH-ABIS.HTML for more information) Die Verwendung des NDK kann ein effektiver Weg sein existierende C/C++ Bibliotheken in seine Applikation einzubetten. Ein Programm das mit dem NDK geschrieben wurde beinhaltet eine NativeActivity, welche in der Manifestdatei unter dem Tag meta-data, des Tags activity mit dem Attribut android:value="native-activity" deklariert wird. In der NativeActivity wird dann in C/C++ kodiert [NDK 2011].