Android: von Layouts und Locations

  • Erstellt von Andreas Flügge
  • Development

Der Einstieg in eine komplett neue Programmier- und Laufzeit-Umgebung ist manchmal nicht ganz einfach. Bis eine Anwendung zum Laufen gebracht wird, sind neben dem Umgang mit der API auch Kenntnisse zur Konfiguration und zum Umgang mit der Entwicklungs-Umgebung notwendig.

Um den Einstieg in die Applikationsentwicklung mit Android zu demonstrieren, zeigt der Artikel eine bewusst einfach gehaltene Beispiel-Anwendung. Das Gerüst der vom Wizard generierten Anwendung wird anschließend erweitert und die gewünschte Funktionalität hinzugefügt.

MyLocation

Für einen ersten Überblick wird zunächst festgehalten, was die zu entwickelnde Applikation „MyLocation“ leisten soll. Im Wesentlichen besteht sie aus einer View, die den aktuellen Status des GPS-Empfängers in Form der zuletzt festgestellten Geo-Koordinaten darstellt. Das klingt nicht besonders anspruchsvoll, aber auf dem Weg zur lauffähigen Anwendung gilt es, die eine oder andere Hürde zu überwinden. Ausgangspunkt ist das Code-Gerüst, das mit dem Wizard des Android Software Development Kit (SDK) erstellt wurde.

Layout und Views

Das Layout von Android-Anwendungen, das zunächst das grundsätzliche Aussehen einer Anwendung bestimmt, wird standardmäßig in XML-Dateien gespeichert. Der Wizard hat beim Anlegen unserer Anwendung bereits das Default-Layout erstellt, es liegt in der Datei „main.xml“ im Verzeichnis „res/layout“. Für die Bearbeitung des Layouts stellt das SDK einen grafischen Editor zur Verfügung. Alternativ lassen sich die XML-Dateien natürlich auch mit jedem beliebigen Text-Editor bearbeiten.

Das Layout soll einfach gehalten werden, alle Elemente werden untereinander dargestellt. Der Umgang mit dem Editor bedarf einer gewissen Einarbeitung, die Bedeutung der einzelnen Elemente und Attribute findet man auf den Referenzseiten des SDK. Für unsere Applikation bedienen wir uns des „LinearLayout“, in das wir den Applikationsnamen, die zwei Titel- und Werttexte sowie den Button platzieren. Der Button wird zunächst ohne Funktion bleiben, kann aber später für Erweiterungen genutzt werden.

Mit den Attributen der einzelnen Elemente sorgen wir für die linksbündige Ausrichtung der Label, die rechtsbündige Ausrichtung der Werte und die Zentrierung von Applikationsnamen und Button. Das dazugehörige Layout wird im Editor, wie in Abbildung 1 zu sehen ist, dargestellt. Die XML-Darstellung entspricht dem Listing 1. 

//CODE<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:orientation="vertical"
    android:layout_height="wrap_content">
  <TextView 
      android:layout_height="wrap_content"
      android:layout_margin="10px"
      android:textSize="8pt"
      android:text="@string/app_name"
      android:layout_width="wrap_content"
      android:gravity="center_horizontal"
      android:layout_gravity="center_horizontal"
      android:textStyle="bold"
      android:id="@+id/TitleTextView"/>
<TextView
      android:layout_height=“wrap_content“
      android:layout_margin=“5px“
      android:textSize=“6pt“
      android:text=“@string/breiteString“
      android:layout_width=“wrap_content“
    android:layout_gravity=“left“
      android:textStyle=“bold“
      android:id=“@+id/BreiteTextView“/>
...
  <Button
    android:id="@+id/KarteButton"
    android:text="@string/karteString"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:layout_margin="10px">
  </Button>
</LinearLayout>//CODE

 Listing 1: Layout in main.xml

Die dort referenzierten Texte befinden sich in der Datei „strings.xml“ im Verzeichnis „res/values“ (siehe Listing 2). Diese können ebenfalls mit dem im SDK enthaltenen Editor oder auch textuell bearbeitet werden. Will man die Anwendung später internationalisieren, muss lediglich eine sprachspezifische Version dieser Datei erstellt und dann in einer leicht modifizierten Verzeichnisstruktur („res/values-<länderkürzel>“) abgelegt werden. Das Android-Framework sorgt anhand der Spracheinstellungen des Geräts dann für die Verwendung der passenden Datei.

//CODE<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">MyLocation</string>
    <string name="laengeString">Länge</string>
    <string name="breiteString">Breite</string>
    <string name="laengeWert">0.000000</string>
    <string name="breiteWert">0.000000</string>
    <string name="karteString">Zeige auf Karte</string>
</resources>//CODE

Listing 2: Texte in strings.xml

Beim Starten der Applikation in Eclipse (MyLocation -> Run As -> Android Application), wird die Anwendung im Emulator installiert und gestartet. Übersetzt und gebaut wird sie automatisch mit den Standardeinstellungen bei jeder Änderung. Wir sehen die erstellte View auf dem virtuellen Gerät. Diese sieht noch reichlich unspektakulär aus, denn es werden nur die Default-Daten angezeigt und hinter dem Button liegt keine Funktionalität.

Standortbestimmung

Im nächsten Schritt möchten wir erreichen, dass die Anzeige automatisch aktualisiert wird, sobald sich der momentane Standort verändert. Android stellt zu diesem Zweck einige Klassen zur Verfügung, die das Auslesen des GPS-Device ermöglichen und die Applikation darüber informieren können.

Im Package „android.location“ befinden sich die zur Standortbestimmung benötigten Klassen. Von zentraler Bedeutung ist hier der „LocationManager“. Dieser ist in der Lage, vom System einen Dienst anzufordern, der uns Geo-Informationen liefern kann. Diese Klasse („LocationManager“) wird allerdings nicht direkt instanziiert. Die Methode „getSystemService(Context.LOCATION_SERVICE)“ liefert eine solche Instanz, die wir unter anderem dazu benutzen können, um uns über veränderte Geopositionen informieren zu lassen.

Grundlage dafür ist, dass wir eine Klasse mit dem Interface „LocationListener“ implementieren lassen, die über die Methode „onLocationChanged“ die Informationen über die veränderte Position verarbeiten kann. Das Interface verlangt die Implementierung weiterer Methoden, die in unserem Fall aber ohne Funktionalität bleiben können. Der Einfachheit halber machen wir dies direkt mit unserer Haupt-Activity „MyLocation“.

Die Methode „onLocationChanged“ in unserem Bespiel aktualisiert lediglich die Werte („Texte“) in der View. Sie stellt mithilfe der Methode „findViewById“ die Views der Koordinatenwerte in der „Activity“ fest und ändert ihre Inhalte entsprechend. Jetzt fehlt nur noch die Registrierung unserer „Activity“ als „LocationListener“. Dazu  nutzen wir die beiden Lebenszyklus-Methoden „onResume“ und „onPause“. Wir erinnern uns: Wird eine „Activity“ sichtbar, so wird deren Methode „onResume“ aufgerufen und sie wechselt in den Status „aktiv“. Wird sie hingegen durch andere Komponenten verdeckt, wechselt sie in den Status „inaktiv“, nachdem die Methode „onPause“ passiert wurde. Praktisch bedeutet dies, dass wir in unserer „Activity“ beide Methoden überschreiben und die Registrierung bzw. Deregistrierung unseres Listeners in der jeweiligen Methode durchführen.

Energiespar-Aspekte

Die Klasse „LocationManager“ bietet dazu die beiden Methoden „requestLocationUpdates“ und „removeUpdates“ an. Durch dieses Vorgehen garantieren wir ein möglichst energieeffizientes Arbeiten unserer Applikation. Die Daten werden nur dann angefordert und ausgewertet, wenn die Anwendung auch wirklich aktiv ist.

Die Registrierungsmethode verlangt als Parameter den Typ des Location Providers  (neben dem GPS-Empfänger kann es noch weitere geben), den zu registrierenden Listener sowie Angaben (Zeit und Entfernung) zum Intervall der Aktualisierung. Diese letzten beiden Informationen haben ebenfalls Auswirkungen auf den Energieverbrauch und sollten je nach Anwendungsfall geeignet gewählt werden. In unserem Beispiel wird der Einfachheit halber eine ständige Aktualisierung parametrisiert, die aber auch einem maximalen Energiebedarf entspricht. Listing 3 zeigt den Code für  unsere Activity.

//CODEpublic class MyLocation extends Activity implements LocationListener {
    LocationManager locationManager;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        locationManager=(LocationManager)getSystemService(
            Context.LOCATION_SERVICE);
    }
    @Override
    protected void onPause() {
        super.onPause();
        locationManager.removeUpdates(this);
    }
    @Override
    protected void onResume() {
        super.onResume();
        locationManager.requestLocationUpdates(
            LocationManager.GPS_PROVIDER, 0, 0, this);
    }
    @Override
    public void onLocationChanged(Location location) {
        TextView breiteView = (TextView)findViewById(
            R.id.BreiteWertView);
        TextView laengeView = (TextView)findViewById(
            R.id.LaengeWertView);
    breiteView.setText(String.valueOf(
        location.getLatitude()));
    laengeView.setText(String.valueOf(
        location.getLongitude()));
    }
    /* unwichtige Methoden ausgelassen ... */
}//CODE

Listing 3: MyLocations-Activity

Berechtigung

Um die Applikation starten zu können, ist jetzt noch ein letzter Schritt notwendig. Durch die Nutzung der Location-API haben wir unserer Anwendung eine Fähigkeit verliehen, die eine besondere Berechtigung verlangt, nämlich die Nutzung des GPS-Empfängers. Das Android-Sicherheitskonzept kennt ungefähr hundert verschiedene Berechtigungen, die dem Anwender zeigen, welche Aktivitäten eine zu installierende Applikation auf seinem Gerät ausführen darf. Jede dieser Berechtigungen wird vom Entwickler im Manifest der Anwendung registriert. Vergisst er dies, beendet sich die Anwendung, sobald eine entsprechende Funktion im Code verwendet wird, mit einer Exception.

Das Manifest ist ebenfalls eine XML-Datei. Das SDK bringt aber auch hierfür einen eigenen Editor mit. In unserem Fall müssen wir die Berechtigung „ACCESS_FINE_LOCATION“ hinzufügen. Listing 4 zeigt das entstandene Manifest.

//CODE<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"     
      package="de.objectsystems.mylocation"     
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="8" />
    <uses-permission
android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>   
    <application android:icon="@drawable/icon"
android:label="@string/app_name">
        <activity android:name=".MyLocation"
                  android:label="@string/app_name">
          <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
        </activity>
    </application>
</manifest>//CODE

Listing 4: AndroidManifest.xml

Test im Emulator

Für einen ersten Test benötigen wir kein reales Android-Device, der Emulator hat die Möglichkeit, für unsere Anwendung GPS-Koordinaten zu simulieren. Dazu wechseln wir nach dem Start der Applikation in die DDMS-Perspektive (Dalvik Debug Monitor Service). Dort befinden sich im linken unteren Bereich des Emulator-Control-Reiters die Location Controls. Hier eingegebene Koordinaten werden nach Betätigung des Send-Buttons dem virtuellen Device als neue Position übermittelt. Wie erwartet, werden daraufhin die Felder unserer Activity aktualisiert (siehe Abbildung 2).

Fazit

Wir haben gesehen, wie eine Android-Applikation prinzipiell aufgebaut ist. Die Funktionsweise von Layouts und Views sowie das Auslesen des GPS-Empfängers haben wir ebenso betrachtet wie die Auswirkungen der Nutzung von sicherheitsrelevanten Funktionen auf das Manifest. Wir konnten aufzeigen, dass wir mit relativ wenig Source-Code in kurzer Zeit eine voll funktionsfähige Anwendung erstellen können und diese auch ohne physisches Gerät trotz hardwarenaher Funktionen sehr gut testen konnten.

Es können natürlich nicht alle Aspekte einer Android-Applikation beleuchtet werden. Aber mit der entwickelten Anwendung haben wir eine Basis gelegt, die hoffentlich zum weiteren Experimentieren mit Android anregt.