Seminararbeit - RWTH

Werbung
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].
Herunterladen