Samstag, 1. März 2014

Google Cloud Endpoints - Verstärkung für Android Apps (Teil 2)

Im ersten Beitrag dieser Reihe zum Thema Cloud Endpoints haben wir einen ersten einfachen Service implementiert und im Browser getestet. Unser nächstes Ziel besteht darin, den Service in unsere App 'Artist' zu integrieren. Das Ergebnis sieht dann wie in der Abbildung aus. Die Daten für den Namen und das Gründungsjahr einer Band werden von unserem Service versorgt. Die App präsentiert dann nur noch die Daten.



Die Artists-App im Android Emulator




Änderungen am Layout

Da wir das Projekt mit dem Standard-Template angelegt hatten, sollte es eine Layout-Datei namens activity_main.xml in unserem res/layout-Verzeichnis geben. Den Inhalt des TextView-Tags ändern wir in dieser Datei geringfügig: Es wird nicht mehr 'Hello Word', sondern ein anfangs leerer Text angezeigt. Ausserdem bekommt die View eine ID, über die wir sie später mit Daten bestücken:

<TextView
  android:id="@+id/band"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:text="" />
 

Zum Service passende Bibliotheken?

Nach dem Android-Projekt, hatten wir ein zugehöriges Backend-Projekt erzeugt. Das hatte zur Folge, dass der Wizard verschiedene Demo-Klassen zum Android-Projekt hinzugefügt hat. Außerdem sehen wir im Android-Projekt noch Pakete, deren Namen mit dem Präfix endpoint-libs anfangen: Damit Services genutzt werden können, müssen die zugehörigen Datentypen auch am Client, also der Android App, bekannt sein. Wir hatten aber alle generierten Services am Server gelöscht und durch eigene ersetzt. Die Bibliotheken am Client repräsentieren also noch den initialen Zustand und sind nicht mehr aktuell.

Altlasten bereinigen

Es ist ganz einfach den Client an den neuen Service anzupassen. Rechtsklick auf das Artists-AppEngine Projekt, im Menüpunkt 'Google' den Punkt 'Generate Cloud Endpoint Client Library' wählen und alles passt wieder. Im Android-Projekt gibt es jetzt bei den Endpoint-Bibliotheken den Ordner bandsservice-v1-generated-source, der die Klassen BandsService und Band enthält, die uns bekannt vorkommen sollten.

Im Android-Projekt tauchen nun Fehlermeldungen auf: Zwei der 'alten' Klassen finden benötigte Typen nicht mehr in den endpoint-libs. Kein Problem! Wir brauchen die beiden Klassen GCMIntentService und RegisterActivity nicht mehr und löschen sie. Lediglich CloudEndpointUtils wird uns - wie der Name schon sagt - noch nützlich sein. Diese Klasse enthält beispielsweise auch Methoden, die die Verbindung zum Server herstellen. Da wir zunächst mit einem lokal Server arbeiten, ändern wir die Konstante LOCAL_ANDROID_RUN auf den Wert true.

Die Activitiy initialisieren

Um unsere Service-API zu nutzen, müssen wir die Klasse MainActivity anpassen:

public class MainActivity extends Activity {

  private BandsService endpoint = null;
  private TextView bandTV;
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    bandTV=(TextView) findViewById(R.id.band);
   
    BandsService.Builder endpointBuilder = new BandsService.Builder(
      AndroidHttp.newCompatibleTransport(), new JacksonFactory(),
      new HttpRequestInitializer() {
        public void initialize(HttpRequest httpRequest) {
        }
      });

    endpoint = CloudEndpointUtils.updateBuilder(endpointBuilder).build();
    ...

Die ersten Zeilen der onCreate-Methode führen die gewohnten Initialisierungsarbeiten durch. Bei der Initialisierung der Variablen endPointBuilder handelt es sich um einfachen Boilerplate Code, um den Builder unseres Services zu erzeugen. Echte Funktionalität wird nicht hinzugefügt. In der letzten Zeile wird dann das Attribut endpoint zum Proxy für unsere serverseitige API gemacht.
  

Asynchroner Netzwerkzugriff

Bis jetzt hat sich die App noch nicht mit dem Netzwerk verbunden. Es wäre auch nicht wünschenswert gewesen. Seit Android 3 ist nämlich der Strict-Mode standardmäßig aktiviert. Dieser sorgt dafür, dass die App Exceptions wirft, sobald der GUI-Thread der App Ein- oder Ausgaben über Speichermedien oder Netzwerke durchführt. Das soll dafür sorgen, dass Entwickler solche Aufgaben in eigene Threads auslagern und so ein bessere User Experience ohne wartende GUI-Threads bieten.

Es gibt jetzt verschiedene Möglichkeiten zur asynchronen Programmierung unter Android, die Arbeit mit der Klasse AsyncTask bietet viele Vorteile. Das Muster sieht vor, dass man eine eigene private AyncTask-Klasse in der Activity definiert:

  private class EndpointTask extends AsyncTask<Void, Void, Band> {
   
    @Override
    protected Band doInBackground(Void... params) {
      Band result = null;
      try {
        result = endpoint.bandsAPI().getBand(0).execute();
      } catch (IOException e) {
        e.printStackTrace();
      }
      return result;
    }

    protected void onPostExecute(Band band) {
      String text = band.getName()+": "+ band.getFounded();
      bandTV.setText(text);
    }
  }
 
Die Methode doInBackground startet automatisch einen neuen Thread. Die Methode execute  baut die Verbindung zum Server auf, von dem dann mit getBand(0) die Daten, die zum Datensatz mit der ID 0 gehören abgerufen werden. Wenn doInBackground und damit der Thread beendet sind, wird onPostExecute im GUI-Thread abgearbeitet und die TextView mit den ermittelten Daten versorgt.

Wir dürfen jetzt nicht vergessen die AsyncTask zu starten. Die letzten beiden Zeilen in der onCreate Methode lauten:

  ...
  new EndpointTask().execute();
}

Fast fertig

Die App macht jetzt das was sie soll. Naja, fast: Der Server läuft immer noch lokal auf unserer Entwicklungsmaschine und die Daten sind nicht persistiert, sondern werden in einem Array gehalten. Diese beiden Defizite werden in den folgenden Artikeln beseitigt werden.






Keine Kommentare:

Kommentar veröffentlichen