Spielzeug für Nichtspieler

Android: Ein Blick auf die fünfte Version der Play Services

Tam Hanna
© Shutterstock/Twin Design

Play ist ein ungünstiges Präfix: Auguren führen den Misserfolg des BlackBerry PlayBooks unter anderem auf den Namen zurück, der konservativen IT-Managern sauer aufstieß. Die soeben in der fünften Version erschienenen Google Play Services hatten damit bisher kein Probleme.

Android leidet seit jeher darunter, dass Hardwarehersteller ihre Telefone nur sehr zögerlich mit Updates versorgen. Aus kommerzieller Sicht ist eine Aktualisierung reine Zeitverschwendung: Sie bringt keine Zusatzeinnahmen und hält Nutzer vom Kauf eines Nachfolgemodells ab. Zur Behebung dieses Problems beschloss Google im Jahr 2012, dass das Betriebssystem fortan in mehrere Komponenten zerlegt wird. Eine als Play Services bezeichnete Komponente kommt in Form einer aus dem Play Store herunterladbaren App aufs Smartphone. Dadurch kann Google die dort enthaltenen Funktionen aktualisieren, ohne dabei auf seine Hardwarepartner angewiesen zu sein.

Als angenehmen Nebeneffekt ließ sich dies zur Isolation von „nicht freigegebenen Builds“ des Betriebssystems nutzen. Wenn ein auf AOSP basierender Build – Stichworte Amazon und Kindle bzw. Fire OS – nicht in den Play Store kann, so sind seine Nutzer von den Features ausgeschlossen. Im Laufe der Zeit haben sich die anfangs als kleine Clientbibliothek vorgesehenen Services in einen Behemoth verwandelt. Neben den von Anfang an inkludierten Google+-APIs finden Entwickler dort soziale Dienste für Spiele, Mapping-Komponenten und diverse andere mehr oder weniger nützliche Tools.

Tiefe Suche

Palm OS beeindruckte mit einer als Global Find bezeichneten Funktion. Sie analysierte die in den diversen Applikationen befindlichen Informationen unter Mithilfe des jeweiligen Programms, was eine wesentliche Verbesserung der Suchergebnisse mit sich brachte. Android bietet ein derartiges Feature an, das bisher leider nur wenig Entwickleraufmerksamkeit bekam. Google scheint die Schnittstelle mittlerweile sogar als so unwichtig zu betrachten, dass sie in Android 4.4 fehlerhaft implementiert wurde.
 
Die in Zusammenarbeit mit der Google-Search-App (erst ab Version 3.6) angebotene App Indexing API geht an dieser Stelle einen Schritt weiter. App Indexing unterscheidet sich vom weiter angebotenen Search Listener insofern, als Googles Suchsystem die Applikationen im Fall einer Suchanfrage nicht aufruft. Die Engine arbeitet mit einer Konserve, deren Daten von den Apps während ihrer Ausführung aufgefüllt werden. Als ersten Schritt zur Nutzung des Diensts müssen Sie die Manifest-Datei Ihres Programms mit Listenern ausstatten, die eingehende Aufrufe entgegennimmt. Die Grundstruktur des Codes ist in Listing 1 illustriert.

<activity android:name="com.example.android.GizmosActivity"
           android:label="@string/title_gizmos" >
     <intent-filter android:label="@string/filter_title_viewgizmos">
         <action android:name="android.intent.action.VIEW" />
         <data android:scheme="http"
               android:host="example.com"
               android:pathPrefix="/gizmos" />
         <category android:name="android.intent.category.DEFAULT" />
         <category android:name="android.intent.category.BROWSABLE" />
     </intent-filter>
 </activity>

Neben den diversen Einstellungen ist an dieser Position die Deklaration von zwei Kategorien interessant. BROWSABLE weist das Betriebssystem dazu an, aus der Suche eingehende Anfragen mit der Applikation zu verdrahten. Wer – wie hier gezeigt – auch Default übergibt, erhält eine App, die sich auch von beliebigen Webbrowsern ansprechen lässt.

Die onStart()– und onStop()-Routinen des Programms werden im nächsten Schritt um Methodenaufrufe erweitert, die die Such-Engine über die am Bildschirm befindlichen Inhalte informieren. Die in der Dokumentation als „Outlinks“ bezeichneten Parameter teilen Google mit, welche Seiten von dieser View aus ansprechbar sind. Diese Informationen helfen dem Suchsystem, eine Art „Sitemap“ ihres Programms zu erstellen (Listing 2).

@Override
  public void onStart(){
    ...

    // Connect your client
    mClient.connect();

    // Define the outlinks on the page for both app and web
    Uri appUri1 = Uri.parse("android-app://com.google.developers/appindexingdemo/restaurant1/menu/1");
    Uri webUrl1 = Uri.parse("http://appindexingdemo.google.com/item/1");

    Uri appUri2 = Uri.parse("android-app://com.google.developers/appindexingdemo/restaurant1/address/2");
    Uri webUrl2 = Uri.parse("http://appindexingdemo.google.com/item/2");

    // Create App Indexing Link objects
    AppIndexingLink item1 = new AppIndexingLink(appUri1, webUrl1, this.findViewById(R.id.btn1));
    AppIndexingLink item2 = new AppIndexingLink(appUri2, webUrl2, this.findViewById(R.id.btn2));

    // Create a list out of these objects
    List<AppIndexingLink> outlinks = new ArrayList<AppIndexingLink>();
    outlinks.add(item1);
    outlinks.add(item2);

    // Define a title for your current page, shown in autocompletion UI
    final String TITLE = "App Indexing API Title";

    // Call the App Indexing API view method
    AppIndex.AppIndexApi.view(mClient, this, APP_URI, TITLE, WEB_URL, outlinks);

    ...
  }

  @Override
  public void onStop(){
  ...

    // Call viewEnd() and disconnect the client
    AppIndex.AppIndexApi.viewEnd(mClient, this, APP_URI);
    mClient.disconnect();

    ...
  }

Beachten Sie, dass Google die missbräuchliche Verwendung des API sanktioniert. Als wichtigste Hausregel hat der Suchmaschinenanbieter etabliert, dass die diversen Funktionen nur einmal pro „Formular“ aufgerufen werden dürfen. Allgemein gilt die schon aus dem Webbereich bekannte Regel, dass Google alles verbietet, was „guten Suchergebnissen“ entgegenläuft.

Spiele intelligent!

Wer Spiele entwickelt, macht über kurz oder lang mit „elastischer Nachfrage“ Bekanntschaft. User laden Spiele herunter, um Zeit totzuschlagen – welcher der tausenden von Titeln auserkoren wird, wird durch Herdenverhalten und Store-Management entschieden. Aus diesem Grund ist es besonders wichtig, einmal akquirierte Zocker so lange wie möglich bei der Stange zu halten. Dabei spielt das Anbieten von Missionen eine wichtige Rolle. Bisher war zum Freischalten neuer Einsätze ein Update der Applikation erforderlich. Ein als Events and Quests bezeichnetes Feature verspricht, hier Abhilfe zu schaffen. Zur Implementierung dieses Subsystems müssen Sie das Spiel mit Events versehen. Dabei handelt es sich um Ereignisse, die im Rahmen des Spielgeschehens so oder so anfallen. Im Fall eines Restaurantmanagers wäre dies beispielsweise der Verkauf eines Getränks – bei einem Egoshooter könnte die Erledigung eines Feindes als Event dienen:

public void submitEvent(String eventId)
{
    String myEventId = eventId;
    Games.Events.increment(this.getApiClient(), myEventId, 1);
}

Die Nutzung von Events setzt die Anmeldung im Backend voraus. Googles Server weisen den einzelnen Ereignissen Event-IDs zu, die wie im Snippet gezeigt in die increment-Routine wandern. Die als letztes übergebene Zahl bestimmt die Anzahl der hinzuzufügenden Events – die Mitteilung der Tötung von 100 Zombies lässt sich auch ohne eine for-Schleife bewerkstelligen. Quests entstehen „im nächsten Schritt“ im Backend. Neben der Festlegung der für die Erfüllung notwendigen Kriterien und den diversen für das GUI notwendigen Informationen und Bildern dürfen Sie hier auch einen beliebigen String übergeben. Dieser wird von Google 1:1 an die App weitergereicht, wenn der Nutzer die Bedingungen des Quests erfüllt hat – normalerweise handelt es sich dabei um einen JSON-String, der die angebotenen Belohnungen quantifiziert. Die Play Services enthalten ein standardisiertes Formular, in dem der Benutzer eine Liste der für ihn gültigen Quests erhält. Es lässt sich durch das Abfeuern des dazugehörenden Intents aufrufen:

public void showQuests()
{
    Intent questsIntent = Games.Quests.getQuestsIntent(this.getApiClient(),
            Quests.SELECT_ALL_QUESTS);
    startActivityForResult(questsIntent, 0);
}

Zum Entgegennehmen der eingehenden Ereignisse müssen Sie nach dem Start des Spiels einen Completition-Listener anmelden. Da dies erst nach der Anmeldung bei Google Play möglich ist, sollten Sie wie im von Google vorgegebenen Snippet vorgehen:

@Override
public void onSignInSucceeded() {
    Games.Quests.registerQuestUpdateListener(this.getApiClient(), this);
}

Der erfolgreiche Abschluss eines Quests wird sodann durch einen Aufruf der übergebenen Methode angezeigt. Sie muss die Belohnung durch Aufruf von claim() einfordern, und einen eventuellen Belohnungs-String parsen und an den Benutzer „auszahlen“ (Listing 3).

@Override
public void onQuestCompleted(Quest quest) {
    Games.Quests.claim(this.getApiClient(), quest.getQuestId(),
            quest.getCurrentMilestone().getMilestoneId());
    try {
        String reward = new
                String(quest.getCurrentMilestone().getCompletionRewardData(),
                "UTF-8");

Beachten Sie dabei bitte, dass der Inhalt des Strings nicht überprüft wird. „Bösartige Nutzer“ könnten den eingehenden Wert mit einem Packet Sniffer abfangen und den erfolgreichen Abschluss später mit veränderten Belohnungswerten „nachspielen“. Daraus folgt, dass Sie dem Zocker als Belohnung nur „wertlose“ Gegenstände anbieten dürfen.

Aufmacherbild: Boy with toy cars von Shutterstock / Urheberrecht: Twin Design

[ header = Seite 2: Spielstand auf Lepschi ]

Spielstand auf Lepschi

Windows 8.1 trat an, um Entwicklern eine einheitliche Plattform anzubieten. Apps sollten auf Tablets, Smartphones und dem mit einer Xbox One versorgten Smart TV so identisch wie irgendwie möglich funktionieren. Dazu ist es hilfreich, wenn die verschiedenen zu einem Nutzer gehörenden Instanzen eines Programms gemeinsame Daten direkt austauschen können.

Für Entwickler ist die Realisierung eines derartigen Services alles andere als einfach. Die Privatsphäreneinstellungen der Betriebssysteme verhindern die „Korrelation“: Der Server kann nicht  ohne Weiteres feststellen, zu welchem Smart TV ein Telefon oder Tablet gehört. Microsoft löste dieses Problem mit einem Roaming genannten Dienst, der in den Play Services über das Saved-Games-API nachgebildet wird. Als „Speichersystem“ kommt hier – anders als bei Microsoft – die Drive-Partition des gerade eingeloggten Nutzerkontos zum Einsatz. Dies führt zu einer großzügigeren Platzquote; dank einer Art DRM kann der User die Dateien trotzdem nicht ohne Weiteres bearbeiten und verändern. Für den Drive-Zugriff müssen Sie im Rahmen der Initialisierung einige Deklarationen vornehmen. Schreiben Sie dem API-Client die in Listing 4 gezeigten Eigenschaften ein.

    GoogleApiClient.Builder builder = helper.getApiClientBuilder();
    GoogleApiClient.Builder builder = new GoogleApiClient.Builder(
            mActivity, this, this);
    builder.addScope(Drive.SCOPE_APPFOLDER);
    builder.addApi(Drive.API);

Für die Interaktion mit dem Benutzer bietet Google – wie auch bei den Quests – eine Gruppe von hauseigenen Formularen an, die im Rahmen des Ladens der Play-Bibliothek automatisch bereitgestellt werden. Die Rolle des Entwicklers beschränkt sich darauf, diese auf den Bildschirm zu bringen und die Entscheidungen des Benutzers mit der hauseigenen Logik zu verdrahten. Im Fall des Ladens müssen Sie den Snapshot durch Aufruf der open()-Funktion öffnen und die in ihm enthaltenen Informationen im nächsten Schritt auslesen. Die Auswertung der dort enthaltenen Bytes ist die alleinige Verantwortung des Entwicklers (Listing 5.)

protected Integer doInBackground(Void... params) {
            Snapshots.OpenSnapshotResult result = Games.Snapshots.open(getApiClient(),
                    currentSaveName, true).await();
            int status = result.getStatus().getStatusCode();
            if (status == GamesStatusCodes.STATUS_OK) {
                Snapshot snapshot = result.getSnapshot();
                // Read the byte content of the saved game. 
                mSaveGame = new SaveGame(snapshot.readFully());
            }else{
                Log.e(TAG, "Error while loading: " + status);
            }
            return status;
        }

Das Speichern neuer Save Games ist insofern kritischer, als die von Google vorgegebene Benutzerschnittstelle dem User einige Metainformationen und einen Screenshot des Spielstands anbieten möchte. In Listing 6 finden Sie alle wichtigen Felder – der an diese aus der Dokumentation übernommene Methode übergebene Snapshot muss vorher durch Aufruf von open() geöffnet werden.

private String writeSnapshot(Snapshot snapshot){
    // Set the data payload for the snapshot. 
    snapshot.writeBytes(mSaveGame.toBytes());
    // Save the snapshot. 
    SnapshotMetadataChange metadataChange
            = new SnapshotMetadataChange.Builder()
            .setCoverImage(getScreenShot())
            .setDescription("Player on Level 1-3 with 100 gold")
            .build();
    Games.Snapshots.commitAndClose(getApiClient(), snapshot,
            metadataChange);
    return snapshot.toString();
}

Da Smartphones während des Spielens offline sein können, besteht die Möglichkeit von Kollisionen. Die Behebung derartiger Konflikte liegt in der alleinigen Verantwortung des Entwicklers – von Seiten der Programmierschnittstelle werden sie nur insofern unterstützt, als ihnen das API die beiden „konfliktbehafteten“ Einträge vorlegt. Wichtig ist, dass die Saved Games nicht per REST realisiert sind. Der Zugriff auf die Spielstände ist nur von Android und iOS her möglich und setzt die von Google bereitgestellten Clientbibliotheken voraus.

Sprechen Sie Armbanduhr?

Google nutzt die neue Gerätegattung „Wearable“ zum Abschneiden einiger alter Zöpfe. Wer mit ADT entwickelt, bleibt außen vor. Umsteiger von WristPDA und Co. müssen zudem mit einer wichtigen konzeptuellen Änderung zurechtkommen. Applikationen für Wearables sind in Googles Ökosystem keine Standalone-Programme. Es handelt sich dabei viel mehr um kleine „Modülchen“ von normalen Phone-Applikationen, die im Rahmen des Deployments der Mutter-App auf die Smartwatch wandern. Diese Vorgehensweise ist insofern sinnvoll als Google seine Wearables als „Tentakel zum Smartphone“ sieht. Auf der Uhr arbeitende Applikationen sollen die am Handy aufbereiteten Informationen anzeigen, die eigentliche Rechenlast erfolgt im Idealfall am mit einem dicken Akku ausgestatteten Smartphone oder Tablet. Aus Programmierersicht gibt es – bis auf den Zwang zur Nutzung von Gradle – nur wenige Unterschiede. Eine Wearable-App ist im Grunde genommen ein Programm für Smartphones, das auf die folgenden Namespaces nicht zugreifen kann:

• android.webkit
• android.print
• android.app.backup
• android.appwidget
• android.hardware.usb

Zu guter Letzt sei darauf hingewiesen, dass Wear-Applikationen nicht direkt (also per USB-Verbindung) debuggt werden. Die Fehlersuche erfolgt stattdessen über ein mit der Android Wear-App ausgestattetes Smartphone, das mit der Uhr per Bluetooth kommuniziert.

Kleine Änderungen

Neben den wichtigen Neuerungen bringt die fünfte Version der Play Services auch einige kleinere Upgrades mit. Wer auf Basis des Drive-API entwickelt, darf sich über neue Features freuen: die Applikation kann Resultate sortieren und Ordner auch im Offlinemodus erstellen. ChromeCast-basierende Anwendungen dürfen ihre Nutzer fortan per Closed Captions mit textuellen Bildbeschreibungen versorgen. Dies ist nicht nur im Bereich der Accessibility notwendig: Sind Synchronübersetzungen von Filmen zu teuer, kann man sich auch mit Untertiteln abhelfen.

Mit dem Dynamic Security Provider versucht Google zudem, die Sicherheit des Betriebssystems zu verbessern. Die dahinterstehende Idee ist, dass Fehler in Verschlüsselungsalgorithmen und anderen sicherheitsrelevanten Komponenten leichter behebbar sind, wenn sie nicht auf Betriebssystemebene angeboten werden. Gerätehersteller und Entwickler sollten ihre Algorithmen und Funktionen in Form eines Providers anbieten. Dieser wird zur Laufzeit geladen – wenn die NSA eine Routine knackt, so folgt das Update aus dem Play Store. Nutzer von Analytics und Wallet-API dürfen einige neue Funktionen benutzen. Eine genaue Liste der Änderungen finden Sie auf Googles Webseite.

Android, iOS und mehr

Einige der hier besprochenen Funktionen sind – im Großen und Ganzen – als REST-Service realisiert. Ihre Applikation kommuniziert mit Googles Servern per HTTP: Da Google für manche Plattformen Wrapper anbietet, fällt dies dem durchschnittlichen Entwickler nicht auf. Wenn Sie eine Ihrer Applikationen um die Funktionen der Play Services erweitern möchten, so sollten Sie im ersten Schritt die unter https://developers.google.com/games/services/ einsehbare Webseite der Dienste besuchen. Sie erlaubt Ihnen das Herunterladen von SDKs und fertigen Beispielapplikationen für Android, iOS und einen relativ portablen Dialekt von C++. Unter Android arbeitende Entwickler müssen so gut wie nie „frei programmieren“. Laden Sie das von Google angebotene Beispiel herunter und kopieren Sie die relevanten Module in Ihr Programm.

Fazit

Wer sich ernsthaft mit Android-Applikationen auseinandersetzt, implementiert die Play Services über kurz oder lang. Google bietet Entwicklern ein derart attraktives Paket von sonst nur schwer erhältlichen Diensten an, dass sich ein Deployment in den meisten Fällen lohnt. Dank des REST-API lässt sich ein Teil der Funktionen auch unter Windows Phone ansprechen. Die nur per Clientbibliothek ansprechbaren Teile des Diensts erweisen sich als Hemmschuh bei der Zusammenarbeit mit Amazon, BlackBerry und Yandex. Wenn eine derartige Kooperation für Ihr Produkt vorgesehen ist, so sollten Sie sich während der Entwicklung nach Alternativen umsehen. In der Vergangenheit erwiesen sich die Anbieter der Stores dabei übrigens als sehr kooperativ.

Geschrieben von
Tam Hanna
Tam Hanna
Tam Hanna befasst sich seit der Zeit des Palm IIIc mit der Programmierung und Anwendung von Handcomputern. Er entwickelt Programme für diverse Plattformen, betreibt Onlinenewsdienste zum Thema und steht unter tamhan@tamoggemon.com für Fragen, Trainings und Vorträge gern zur Verfügung.
Kommentare

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu: