APK-Erweiterungsdateien

Bei Google Play darf das komprimierte APK, das Nutzer herunterladen, nicht größer als 100 MB sein. Bei den meisten Apps ist dies viel Platz für den gesamten Code und alle Assets der App. Einige Apps benötigen jedoch mehr Speicherplatz für High-Fidelity-Grafiken, Mediendateien oder andere große Assets. Wenn die komprimierte Downloadgröße Ihrer App 100 MB überstieg, mussten Sie die zusätzlichen Ressourcen bisher selbst hosten und herunterladen, wenn der Nutzer die App öffnet. Das Hosten und Bereitstellen der zusätzlichen Dateien kann kostspielig sein und die Nutzerfreundlichkeit ist oft nicht optimal. Um diesen Vorgang für Sie und die Nutzer angenehmer zu gestalten, können Sie bei Google Play zwei große Erweiterungsdateien anhängen, die Ihr APK ergänzen.

Google Play hostet die Erweiterungsdateien für Ihre App und stellt sie dem Gerät kostenlos zur Verfügung. Die Erweiterungsdateien werden am freigegebenen Speicherort des Geräts gespeichert (SD-Karte oder USB-Partition, auch „externer“ Speicher genannt), wo Ihre App darauf zugreifen kann. Auf den meisten Geräten lädt Google Play die Erweiterungsdatei(en) zusammen mit dem APK herunter, sodass Ihre App beim ersten Öffnen der App über alle erforderlichen Funktionen verfügt. In einigen Fällen muss Ihre App jedoch die Dateien bei Google Play herunterladen, wenn die App gestartet wird.

Wenn du keine Erweiterungsdateien verwenden möchtest und die komprimierte Downloadgröße deiner App größer als 100 MB ist, solltest du deine App stattdessen mithilfe von Android App Bundles hochladen. Dort ist eine komprimierte Downloadgröße von bis zu 200 MB möglich. Da bei Verwendung von App Bundles die APK-Generierung und -Signierung bei Google Play aufgeschoben werden, laden Nutzer optimierte APKs außerdem nur mit dem Code und den Ressourcen herunter, die sie zum Ausführen deiner App benötigen. Du musst nicht mehrere APKs oder Erweiterungsdateien erstellen, signieren und verwalten und Nutzer erhalten kleinere, optimierte Downloads.

Übersicht

Jedes Mal, wenn Sie ein APK über die Google Play Console hochladen, können Sie dem APK eine oder zwei Erweiterungsdateien hinzufügen. Jede Datei kann bis zu 2 GB groß sein und ein beliebiges Format haben. Wir empfehlen jedoch die Verwendung einer komprimierten Datei, um während des Downloads Bandbreite zu sparen. Konzeptionell spielt jede Erweiterungsdatei eine andere Rolle:

  • Die main-Erweiterungsdatei ist die primäre Erweiterungsdatei für zusätzliche Ressourcen, die von Ihrer App benötigt werden.
  • Die Patch-Erweiterungsdatei ist optional und für kleine Aktualisierungen der Haupterweiterungsdatei vorgesehen.

Obwohl Sie die beiden Erweiterungsdateien beliebig verwenden können, empfehlen wir, die Haupterweiterungsdatei die primären Assets bereitzustellen und nur selten zu aktualisieren. Die Patch-Erweiterungsdatei sollte kleiner sein und als Patch-Carrier mit jeder größeren Version oder bei Bedarf dienen.

Auch wenn für Ihr App-Update nur eine neue Patch-Erweiterungsdatei erforderlich ist, müssen Sie im Manifest ein neues APK mit einem aktualisierten versionCode hochladen. In der Play Console können Sie keine Erweiterungsdatei in ein vorhandenes APK hochladen.

Hinweis:Die Patch-Erweiterungsdatei ist semantisch mit der Haupterweiterungsdatei identisch. Sie können jede Datei beliebig verwenden.

Format des Dateinamens

Jede Erweiterungsdatei, die Sie hochladen, kann ein beliebiges Format Ihrer Wahl haben (ZIP, PDF, MP4 usw.). Sie können auch das Tool JOBB verwenden, um einen Satz von Ressourcendateien und nachfolgende Patches für diesen Satz zu kapseln und zu verschlüsseln. Unabhängig vom Dateityp betrachtet Google Play sie als intransparente binäre Blobs und benennt die Dateien nach dem folgenden Schema um:

[main|patch].<expansion-version>.<package-name>.obb

Dieses Schema besteht aus drei Komponenten:

main oder patch
Gibt an, ob die Datei die Haupt- oder Patch-Erweiterungsdatei ist. Für jedes APK kann es nur eine Hauptdatei und eine Patchdatei geben.
<expansion-version>
Das ist eine Ganzzahl, die dem Versionscode des APKs entspricht, mit dem die Erweiterung zuerst verknüpft ist. Sie entspricht dem android:versionCode-Wert der App.

„Erstens“ wird betont, da Sie zwar in der Play Console eine hochgeladene Erweiterungsdatei mit einem neuen APK wiederverwenden können, der Name der Erweiterungsdatei ändert sich aber nicht. Stattdessen wird die Version beibehalten, die beim ersten Hochladen der Datei auf sie angewendet wurde.

<package-name>
Der Paketname Ihrer App im Java-Stil.

Angenommen, Ihre APK-Version lautet 314159 und Ihr Paketname lautet com.beispiel.app. Wenn Sie eine Haupterweiterungsdatei hochladen, erhält die Datei den folgenden Namen:

main.314159.com.example.app.obb

Speicherort

Wenn Google Play Ihre Erweiterungsdateien auf ein Gerät herunterlädt, werden sie im freigegebenen Speicherort des Systems gespeichert. Damit das ordnungsgemäße Verhalten gewährleistet ist, dürfen Sie die Erweiterungsdateien nicht löschen, verschieben oder umbenennen. Falls Ihre App den Download selbst aus Google Play ausführen muss, müssen die Dateien genau am selben Speicherort gespeichert werden.

Die Methode getObbDir() gibt den spezifischen Speicherort für Ihre Erweiterungsdateien in der folgenden Form zurück:

<shared-storage>/Android/obb/<package-name>/

Für jede App befinden sich nie mehr als zwei Erweiterungsdateien in diesem Verzeichnis. Eine ist die Haupt-Erweiterungsdatei und die andere die Patch-Erweiterungsdatei (falls erforderlich). Vorherige Versionen werden überschrieben, wenn Sie Ihre App mit neuen Erweiterungsdateien aktualisieren. Seit Android 4.4 (API-Level 19) können Apps OBB-Erweiterungsdateien ohne externe Speicherberechtigung lesen. Für einige Implementierungen von Android 6.0 (API-Level 23) und höher ist jedoch weiterhin eine Berechtigung erforderlich, sodass Sie die Berechtigung READ_EXTERNAL_STORAGE im App-Manifest deklarieren und zur Laufzeit um die Berechtigung bitten müssen:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Ab Android 6 muss die Berechtigung für den externen Speicher zur Laufzeit angefordert werden. Für einige Implementierungen von Android ist jedoch keine Berechtigung zum Lesen von OBB-Dateien erforderlich. Das folgende Code-Snippet zeigt, wie Sie den Lesezugriff prüfen, bevor Sie die Berechtigung für den externen Speicher anfordern:

Kotlin

val obb = File(obb_filename)
var open_failed = false

try {
    BufferedReader(FileReader(obb)).also { br ->
        ReadObbFile(br)
    }
} catch (e: IOException) {
    open_failed = true
}

if (open_failed) {
    // request READ_EXTERNAL_STORAGE permission before reading OBB file
    ReadObbFileWithPermission()
}

Java

File obb = new File(obb_filename);
 boolean open_failed = false;

 try {
     BufferedReader br = new BufferedReader(new FileReader(obb));
     open_failed = false;
     ReadObbFile(br);
 } catch (IOException e) {
     open_failed = true;
 }

 if (open_failed) {
     // request READ_EXTERNAL_STORAGE permission before reading OBB file
     ReadObbFileWithPermission();
 }

Wenn Sie den Inhalt Ihrer Erweiterungsdateien entpacken müssen, löschen Sie die OBB-Erweiterungsdateien anschließend nicht und speichern Sie die entpackten Daten nicht im selben Verzeichnis. Sie sollten die entpackten Dateien in dem mit getExternalFilesDir() angegebenen Verzeichnis speichern. Wenn möglich, sollten Sie jedoch ein Erweiterungsdateiformat verwenden, mit dem Sie direkt aus der Datei lesen können, anstatt die Daten entpacken zu müssen. Wir haben beispielsweise ein Bibliotheksprojekt namens APK Expansion Zip Library bereitgestellt, das Ihre Daten direkt aus der ZIP-Datei liest.

Achtung:Im Gegensatz zu APK-Dateien können alle im freigegebenen Speicher gespeicherten Dateien vom Nutzer und von anderen Apps gelesen werden.

Tipp:Wenn Sie Mediendateien in einer ZIP-Datei packen, können Sie Medienwiedergabeaufrufe für die Dateien mit Einstellungen für Offset und Länge (z. B. MediaPlayer.setDataSource() und SoundPool.load()) verwenden, ohne die ZIP-Datei entpacken zu müssen. Damit dies funktioniert, dürfen Sie beim Erstellen der ZIP-Pakete keine zusätzliche Komprimierung für die Mediendateien vornehmen. Wenn Sie beispielsweise das zip-Tool verwenden, sollten Sie die Option -n verwenden, um die Dateisuffixe anzugeben, die nicht komprimiert werden sollen:
zip -n .mp4;.ogg main_expansion media_files

Downloadvorgang

Meistens lädt und speichert Google Play Ihre Erweiterungsdateien zusammen mit dem APK auf das Gerät. In manchen Fällen kann Google Play die Erweiterungsdateien jedoch nicht herunterladen oder der Nutzer hat zuvor heruntergeladene Erweiterungsdateien gelöscht. In solchen Fällen muss Ihre App die Dateien zu Beginn der Hauptaktivität selbst herunterladen können. Dazu muss eine von Google Play bereitgestellte URL verwendet werden.

Der Downloadvorgang auf einer hohen Ebene sieht so aus:

  1. Der Nutzer installiert deine App über Google Play.
  2. Wenn Google Play die Erweiterungsdateien herunterladen kann, was bei den meisten Geräten der Fall ist, werden sie zusammen mit dem APK heruntergeladen.

    Wenn Google Play die Erweiterungsdateien nicht herunterladen kann, wird nur das APK heruntergeladen.

  3. Wenn der Nutzer deine App startet, muss sie überprüfen, ob die Erweiterungsdateien bereits auf dem Gerät gespeichert sind.
    1. Falls ja, ist Ihre App einsatzbereit.
    2. Falls nicht, muss Ihre App die Erweiterungsdateien über HTTP von Google Play herunterladen. Ihre App muss über den App-Lizenzierungsdienst von Google Play eine Anfrage an den Google Play-Client senden, der den Namen, die Dateigröße und die URL für jede Erweiterungsdatei angibt. Mit diesen Informationen laden Sie dann die Dateien herunter und speichern sie am richtigen Speicherort.

Achtung:Fügen Sie unbedingt den erforderlichen Code zum Herunterladen der Erweiterungsdateien von Google Play für den Fall ein, dass sich die Dateien beim Start der App noch nicht auf dem Gerät befinden. Wie im folgenden Abschnitt zum Herunterladen der Erweiterungsdateien erläutert, haben wir Ihnen eine Bibliothek zur Verfügung gestellt, die diesen Vorgang erheblich vereinfacht und den Download über einen Dienst mit minimalem Code von Ihnen durchführt.

Checkliste für die Entwicklung

Im Folgenden finden Sie eine Zusammenfassung der Aufgaben, die Sie ausführen sollten, um Erweiterungsdateien mit Ihrer App zu verwenden:

  1. Ermitteln Sie zuerst, ob die komprimierte Downloadgröße Ihrer App größer als 100 MB sein muss. Speicherplatz ist wertvoll und Sie sollten die Gesamtgröße der Downloads so klein wie möglich halten. Wenn deine App mehr als 100 MB nutzt, um mehrere Versionen deiner Grafikinhalte für verschiedene Bildschirmdichten bereitzustellen, solltest du stattdessen mehrere APKs veröffentlichen, bei denen jedes APK nur die Assets enthält, die für die Bildschirme erforderlich sind, auf die es ausgerichtet ist. Die besten Ergebnisse bei der Veröffentlichung bei Google Play erzielen Sie, wenn Sie ein Android App Bundle hochladen, das den gesamten kompilierten Code und die Ressourcen Ihrer App enthält, aber die APK-Generierung und -Signierung bei Google Play verzögert.
  2. Bestimmen Sie, welche App-Ressourcen von Ihrem APK getrennt werden sollen, und verpacken Sie sie in einer Datei, die als Haupterweiterungsdatei verwendet werden soll.

    Normalerweise solltest du nur die zweite Patcherweiterungsdatei verwenden, wenn du Aktualisierungen der Haupterweiterungsdatei durchführst. Wenn deine Ressourcen jedoch das Limit von 2 GB für die Haupterweiterungsdatei überschreiten, kannst du die Patchdatei für die restlichen Assets verwenden.

  3. Entwickeln Sie Ihre Anwendung so, dass sie die Ressourcen aus Ihren Erweiterungsdateien im freigegebenen Speicherort des Geräts verwendet.

    Denken Sie daran, dass Sie die Erweiterungsdateien nicht löschen, verschieben oder umbenennen dürfen.

    Wenn für Ihre App kein bestimmtes Format erforderlich ist, empfehlen wir, ZIP-Dateien für Ihre Erweiterungsdateien zu erstellen und dann mithilfe der ZIP-Erweiterungs-ZIP-Bibliothek zu lesen.

  4. Füge der Hauptaktivität deiner App eine Logik hinzu, die prüft, ob sich die Erweiterungsdateien beim Start auf dem Gerät befinden. Wenn sich die Dateien nicht auf dem Gerät befinden, verwenden Sie den App-Lizenzierungsdienst von Google Play, um URLs für die Erweiterungsdateien anzufordern, laden Sie sie herunter und speichern Sie sie.

    Um die Menge an Code, den Sie schreiben müssen, deutlich zu reduzieren und während des Downloads eine gute Nutzererfahrung zu bieten, empfehlen wir, Ihr Downloadverhalten mithilfe der Downloader-Bibliothek zu implementieren.

    Wenn Sie Ihren eigenen Downloaddienst erstellen, anstatt die Bibliothek zu verwenden, dürfen Sie den Namen der Erweiterungsdateien nicht ändern und müssen sie am richtigen Speicherort speichern.

Wenn Sie die App-Entwicklung abgeschlossen haben, folgen Sie der Anleitung zum Testen Ihrer Erweiterungsdateien.

Regeln und Beschränkungen

Das Hinzufügen von APK-Erweiterungsdateien ist eine Funktion, die beim Hochladen Ihrer App über die Play Console verfügbar ist. Wenn Sie Ihre App zum ersten Mal hochladen oder eine App mit Erweiterungsdateien aktualisieren, müssen Sie die folgenden Regeln und Einschränkungen beachten:

  1. Eine Erweiterungsdatei darf nicht größer als 2 GB sein.
  2. Um Ihre Erweiterungsdateien bei Google Play herunterladen zu können, muss der Nutzer Ihre App bei Google Play erworben haben. Falls die App auf andere Weise installiert wurde, stellt Google Play die URLs für Ihre Erweiterungsdateien nicht zur Verfügung.
  3. Wenn Sie den Download aus Ihrer App heraus durchführen, ist die von Google Play für jede Datei angegebene URL für jeden Download eindeutig und läuft kurz nach der Übermittlung an Ihre App ab.
  4. Wenn Sie Ihre App mit einem neuen APK aktualisieren oder mehrere APKs für dieselbe App hochladen, können Sie Erweiterungsdateien auswählen, die Sie für ein vorheriges APK hochgeladen haben. Der Name der Erweiterungsdatei ändert sich nicht – es behält die vom APK erhaltene Version bei, mit der die Datei ursprünglich verknüpft war.
  5. Wenn Sie Erweiterungsdateien in Kombination mit mehreren APKs verwenden, um verschiedene Erweiterungsdateien für verschiedene Geräte bereitzustellen, müssen Sie dennoch separate APKs für jedes Gerät hochladen, um einen eindeutigen versionCode-Wert bereitzustellen und für jedes APK verschiedene Filter zu deklarieren.
  6. Es ist nicht möglich, ein Update Ihrer App durch Ändern der Erweiterungsdateien durchzuführen. Sie müssen ein neues APK hochladen, um Ihre App zu aktualisieren. Falls sich Ihre Änderungen nur auf die Assets in Ihren Erweiterungsdateien beziehen, können Sie Ihr APK aktualisieren, indem Sie einfach versionCode und eventuell auch versionName ändern.

  7. Speichern Sie keine anderen Daten im Verzeichnis obb/. Wenn du Daten entpacken musst, speichere sie an dem in getExternalFilesDir() angegebenen Speicherort.
  8. Löschen Sie die Erweiterungsdatei .obb nicht und benennen Sie sie nicht um, es sei denn, Sie führen eine Aktualisierung durch. Andernfalls lädt Google Play oder Ihre App wiederholt die Erweiterungsdatei herunter.
  9. Wenn Sie eine Erweiterungsdatei manuell aktualisieren, müssen Sie die vorherige Erweiterungsdatei löschen.

Erweiterungsdateien herunterladen

In den meisten Fällen lädt Google Play Ihre Erweiterungsdateien herunter und speichert sie auf dem Gerät, während das APK installiert oder aktualisiert wird. So sind die Erweiterungsdateien verfügbar, wenn Ihre App zum ersten Mal gestartet wird. In einigen Fällen muss Ihre App die Erweiterungsdateien jedoch selbst herunterladen. Dazu fordert sie sie über eine URL an, die Sie als Antwort des App-Lizenzierungsdiensts von Google Play erhalten haben.

Die grundlegende Logik zum Herunterladen Ihrer Erweiterungsdateien lautet wie folgt:

  1. Suchen Sie beim Start der App nach den Erweiterungsdateien am freigegebenen Speicherort (im Verzeichnis Android/obb/<package-name>/).
    1. Wenn die Erweiterungsdateien bereits vorhanden sind, sind keine weiteren Maßnahmen erforderlich und Ihre App kann fortfahren.
    2. Wenn die Erweiterungsdateien nicht vorhanden sind:
      1. Stellen Sie eine Anfrage über die App-Lizenzierung von Google Play, um die Namen, Größen und URLs der Erweiterungsdateien Ihrer App zu erhalten.
      2. Verwenden Sie die von Google Play bereitgestellten URLs, um die Erweiterungsdateien herunterzuladen und zu speichern. Sie müssen die Dateien im freigegebenen Speicherort (Android/obb/<package-name>/) speichern und den genauen Dateinamen verwenden, der in der Antwort von Google Play angegeben wurde.

        Hinweis:Die URL, die Google Play für Ihre Erweiterungsdateien bereitstellt, ist für jeden Download eindeutig und läuft kurz nachdem sie Ihrer App zur Verfügung gestellt werden, ab.

Wenn Ihre App kostenlos (keine kostenpflichtige App) ist, haben Sie den Lizenzierungsservice für Apps wahrscheinlich noch nicht genutzt. Sie dient in erster Linie dazu, Lizenzierungsrichtlinien für deine App durchzusetzen und sicherzustellen, dass der Nutzer das Recht hat, deine App zu verwenden (er hat rechtmäßig bei Google Play dafür bezahlt). Um die Funktionalität der Erweiterungsdatei zu erleichtern, wurde der Lizenzierungsdienst optimiert. Der Lizenzierungsdienst wurde so optimiert, dass Sie eine Antwort auf Ihre App mit der URL der Erweiterungsdateien Ihrer App erhalten, die bei Google Play gehostet werden. Auch wenn Ihre App für Nutzer kostenlos ist, müssen Sie also die Lizenzüberprüfungsbibliothek (License Verification Library, LVL) einbinden, um APK-Erweiterungsdateien verwenden zu können. Wenn Ihre Anwendung kostenlos ist, müssen Sie natürlich keine Lizenzüberprüfung erzwingen. Sie benötigen nur die Bibliothek, um die Anfrage auszuführen, die die URL Ihrer Erweiterungsdateien zurückgibt.

Hinweis:Unabhängig davon, ob Ihre App kostenlos ist oder nicht, gibt Google Play die URL der Erweiterungsdatei nur zurück, wenn der Nutzer Ihre App über Google Play erworben hat.

Zusätzlich zur LVL benötigen Sie einen Satz Code, der die Erweiterungsdateien über eine HTTP-Verbindung herunterlädt und am richtigen Speicherort im freigegebenen Speicher des Geräts speichert. Beim Einbinden dieses Verfahrens in Ihre App gibt es mehrere Punkte, die Sie berücksichtigen sollten:

  • Auf dem Gerät steht möglicherweise nicht genügend Speicherplatz für die Erweiterungsdateien zur Verfügung. Daher sollten Sie dies prüfen, bevor Sie mit dem Download beginnen, und den Nutzer warnen, wenn nicht genügend Speicherplatz vorhanden ist.
  • Dateidownloads sollten in einem Hintergrunddienst erfolgen, damit die Nutzerinteraktion nicht blockiert wird und der Nutzer Ihre App verlassen kann, während der Download abgeschlossen ist.
  • Während der Anfrage und des Downloads können verschiedene Fehler auftreten, die ordnungsgemäß verarbeitet werden müssen.
  • Die Netzwerkverbindung kann sich während des Downloads ändern. Sie sollten diese Änderungen daher selbst vornehmen und den Download nach Möglichkeit fortsetzen.
  • Auch wenn der Download im Hintergrund erfolgt, sollten Sie eine Benachrichtigung zur Verfügung stellen, die den Downloadfortschritt anzeigt, den Nutzer nach Abschluss des Downloads benachrichtigt und ihn nach Auswahl wieder zu Ihrer App zurückbringt.

Um Ihnen diese Arbeit zu vereinfachen, haben wir die Downloader-Bibliothek erstellt, die die URLs der Erweiterungsdateien über den Lizenzierungsdienst anfordert, die Erweiterungsdateien herunterlädt, alle oben aufgeführten Aufgaben ausführt und sogar das Pausieren und Fortsetzen des Downloads ermöglicht. Durch das Hinzufügen der Downloader-Bibliothek und einiger Code-Hooks zu Ihrer Anwendung ist fast die gesamte Arbeit zum Herunterladen der Erweiterungsdateien bereits für Sie codiert. Daher empfehlen wir Ihnen, Ihre Erweiterungsdateien über die Downloader-Bibliothek herunterzuladen, um mit minimalem Aufwand die bestmögliche Nutzerfreundlichkeit zu bieten. In den folgenden Abschnitten wird erläutert, wie Sie die Bibliothek in Ihre Anwendung einbinden.

Wenn du deine eigene Lösung entwickeln möchtest, um die Erweiterungsdateien mithilfe der Google Play-URLs herunterzuladen, folge der Dokumentation zur App-Lizenzierung, um eine Lizenzanfrage durchzuführen, und rufe dann die Namen, Größen und URLs der Erweiterungsdateien aus den Antwortextras ab. Du solltest die Klasse APKExpansionPolicy (in der Bibliothek zur Lizenzüberprüfung enthalten) als Lizenzierungsrichtlinie verwenden. Damit werden die Namen, Größen und URLs der Erweiterungsdateien aus dem Lizenzierungsdienst erfasst.

Informationen zur Downloader-Mediathek

Um APK-Erweiterungsdateien mit Ihrer App zu verwenden und die beste Nutzererfahrung mit minimalem Aufwand für Sie zu ermöglichen, empfehlen wir die Verwendung der Downloader-Bibliothek, die im APK-Erweiterungsbibliothekspaket von Google Play enthalten ist. Diese Bibliothek lädt Ihre Erweiterungsdateien im Hintergrunddienst herunter, zeigt eine Nutzerbenachrichtigung mit dem Downloadstatus an, verarbeitet den Verlust der Netzwerkverbindung, setzt den Download wenn möglich fort und vieles mehr.

Wenn Sie das Herunterladen von Erweiterungsdateien mithilfe der Downloader-Bibliothek implementieren möchten, müssen Sie lediglich Folgendes tun:

  • Erweitern Sie eine spezielle abgeleitete Service-Klasse und eine abgeleitete BroadcastReceiver-Klasse, die jeweils nur einige wenige Codezeilen von Ihnen benötigen.
  • Fügen Sie Ihrer Hauptaktivität eine Logik hinzu, die prüft, ob die Erweiterungsdateien bereits heruntergeladen wurden, löst ggf. den Downloadprozess aus und zeigt eine UI für den Fortschritt an.
  • Implementieren Sie eine Callback-Schnittstelle mit einigen Methoden in Ihrer Hauptaktivität, die Aktualisierungen zum Downloadfortschritt erhält.

In den folgenden Abschnitten wird erläutert, wie Sie Ihre App mithilfe der Downloader-Bibliothek einrichten.

Verwendung der Downloader-Bibliothek vorbereiten

Wenn du die Downloader Library verwenden möchtest, musst du zwei Pakete aus dem SDK Manager herunterladen und deiner App die entsprechenden Bibliotheken hinzufügen.

Öffnen Sie zuerst den Android SDK Manager (Tools > SDK Manager) und laden Sie unter Darstellung & Verhalten > Systemeinstellungen > Android SDK den Tab SDK-Tools herunter:

  • Google Play-Lizenzierungsbibliothek-Paket
  • APK-Erweiterungsbibliothekspaket für Google Play

Erstelle ein neues Bibliotheksmodul für die Bibliothek zur Lizenzüberprüfung und die Downloader-Bibliothek. Führen Sie für jede Bibliothek folgende Schritte aus:

  1. Wählen Sie File > New > New Module aus.
  2. Wählen Sie im Fenster Create New Module (Neues Modul erstellen) die Option Android Library (Android-Bibliothek) und dann Next (Weiter) aus.
  3. Geben Sie einen App-/Bibliotheksnamen wie „Google Play License Library“ oder „Google Play Downloader Library“ an, wählen Sie Minimum SDK-Level und dann Fertig aus.
  4. Wählen Sie File > Project Structure (Datei > Projektstruktur) aus.
  5. Wählen Sie den Tab Properties (Eigenschaften) aus und geben Sie die Bibliothek im Library Repository (Bibliotheks-Repository) aus dem Verzeichnis <sdk>/extras/google/ ein (play_licensing/ für die Lizenzüberprüfungsbibliothek oder play_apk_expansion/downloader_library/ für die Downloader-Bibliothek).
  6. Wählen Sie OK aus, um das neue Modul zu erstellen.

Hinweis:Die Downloader-Bibliothek hängt von der Bibliothek zur Lizenzüberprüfung ab. Achten Sie darauf, die Lizenzüberprüfungsbibliothek zu den Projekteigenschaften der Downloader Library hinzuzufügen.

Alternativ können Sie Ihr Projekt über eine Befehlszeile aktualisieren, um die Bibliotheken einzubeziehen:

  1. Wechseln Sie in das Verzeichnis <sdk>/tools/.
  2. Führen Sie android update project mit der Option --library aus, um Ihrem Projekt sowohl die LVL als auch die Downloader Library hinzuzufügen. Beispiel:
    android update project --path ~/Android/MyApp \
    --library ~/android_sdk/extras/google/market_licensing \
    --library ~/android_sdk/extras/google/market_apk_expansion/downloader_library
    

Wenn Sie Ihrer App sowohl die Lizenzüberprüfungsbibliothek als auch die Downloader Library hinzugefügt haben, können Sie die Möglichkeit zum Herunterladen von Erweiterungsdateien aus Google Play schnell einbinden. Das Format, das Sie für die Erweiterungsdateien auswählen und wie Sie sie aus dem freigegebenen Speicher lesen, ist eine separate Implementierung, die Sie je nach Ihren App-Anforderungen in Betracht ziehen sollten.

Tipp:Das APK-Erweiterungspaket enthält eine Beispiel-App, die zeigt, wie die Downloader-Bibliothek in einer App verwendet wird. Im Beispiel wird eine dritte Bibliothek aus dem APK-Erweiterungspaket verwendet, die APK-Erweiterungs-Zip-Bibliothek. Wenn Sie ZIP-Dateien für Ihre Erweiterungsdateien verwenden möchten, sollten Sie Ihrer App auch die APK-Erweiterungs-ZIP-Bibliothek hinzufügen. Weitere Informationen finden Sie im Abschnitt APK-Erweiterungs-ZIP-Bibliothek verwenden.

Nutzerberechtigungen deklarieren

Um die Erweiterungsdateien herunterladen zu können, benötigt die Downloader-Bibliothek mehrere Berechtigungen, die du in der Manifestdatei deiner App deklarieren musst. Diese sind:

<manifest ...>
    <!-- Required to access Google Play Licensing -->
    <uses-permission android:name="com.android.vending.CHECK_LICENSE" />

    <!-- Required to download files from Google Play -->
    <uses-permission android:name="android.permission.INTERNET" />

    <!-- Required to keep CPU alive while downloading files
        (NOT to keep screen awake) -->
    <uses-permission android:name="android.permission.WAKE_LOCK" />

    <!-- Required to poll the state of the network connection
        and respond to changes -->
    <uses-permission
        android:name="android.permission.ACCESS_NETWORK_STATE" />

    <!-- Required to check whether Wi-Fi is enabled -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

    <!-- Required to read and write the expansion files on shared storage -->
    <uses-permission
        android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    ...
</manifest>

Hinweis:Standardmäßig erfordert die Downloader Library API-Level 4, die APK-Erweiterungs-ZIP-Bibliothek hingegen erfordert API-Level 5.

Downloader-Dienst implementieren

Damit Downloads im Hintergrund ausgeführt werden können, stellt die Downloader-Bibliothek eine eigene Service-Unterklasse namens DownloaderService bereit, die Sie erweitern sollten. Neben dem Herunterladen der Erweiterungsdateien kann DownloaderService auch:

  • Registriert ein BroadcastReceiver, das auf Änderungen an der Netzwerkverbindung des Geräts (die CONNECTIVITY_ACTION-Übertragung) wartet, um den Download bei Bedarf (z. B. aufgrund einer unterbrochenen Verbindung) zu pausieren und den Download nach Möglichkeit fortzusetzen (Verbindung hergestellt).
  • Plant einen RTC_WAKEUP-Alarm, um den Download für den Fall zu wiederholen, in dem der Dienst beendet wird.
  • Erstellt einen benutzerdefinierten Notification, der den Downloadfortschritt und alle Fehler oder Statusänderungen anzeigt.
  • Ermöglicht deiner App, den Download manuell zu pausieren und fortzusetzen.
  • Vor dem Herunterladen der Erweiterungsdateien wird überprüft, ob der freigegebene Speicher bereitgestellt und verfügbar ist, ob die Dateien noch nicht vorhanden sind und ob genügend Speicherplatz vorhanden ist. Die Nutzenden werden benachrichtigt, wenn eine dieser Aussagen nicht zutrifft.

Sie müssen lediglich eine Klasse in Ihrer App erstellen, die die DownloaderService-Klasse erweitert, und drei Methoden überschreiben, um bestimmte App-Details bereitzustellen:

getPublicKey()
Es muss ein String zurückgegeben werden, bei dem es sich um den Base64-codierten öffentlichen RSA-Schlüssel für dein Publisher-Konto handelt, der in der Play Console auf der Profilseite verfügbar ist (siehe Lizenzierung einrichten).
getSALT()
Es muss ein Array mit zufälligen Byte zurückgegeben werden, die von der Lizenzierungs-Policy zum Erstellen einer Obfuscator verwendet werden. So ist sichergestellt, dass die verschleierte SharedPreferences-Datei, in der die Lizenzierungsdaten gespeichert sind, eindeutig und nicht auffindbar sind.
getAlarmReceiverClassName()
Hierbei muss der Klassenname des BroadcastReceiver in Ihrer App zurückgegeben werden, der den Alarm erhalten soll, dass der Download neu gestartet werden sollte. Dies kann passieren, wenn der Downloader-Dienst unerwartet beendet wird.

Hier sehen Sie beispielsweise eine vollständige Implementierung von DownloaderService:

Kotlin

// You must use the public key belonging to your publisher account
const val BASE64_PUBLIC_KEY = "YourLVLKey"
// You should also modify this salt
val SALT = byteArrayOf(
        1, 42, -12, -1, 54, 98, -100, -12, 43, 2,
        -8, -4, 9, 5, -106, -107, -33, 45, -1, 84
)

class SampleDownloaderService : DownloaderService() {

    override fun getPublicKey(): String = BASE64_PUBLIC_KEY

    override fun getSALT(): ByteArray = SALT

    override fun getAlarmReceiverClassName(): String = SampleAlarmReceiver::class.java.name
}

Java

public class SampleDownloaderService extends DownloaderService {
    // You must use the public key belonging to your publisher account
    public static final String BASE64_PUBLIC_KEY = "YourLVLKey";
    // You should also modify this salt
    public static final byte[] SALT = new byte[] { 1, 42, -12, -1, 54, 98,
            -100, -12, 43, 2, -8, -4, 9, 5, -106, -107, -33, 45, -1, 84
    };

    @Override
    public String getPublicKey() {
        return BASE64_PUBLIC_KEY;
    }

    @Override
    public byte[] getSALT() {
        return SALT;
    }

    @Override
    public String getAlarmReceiverClassName() {
        return SampleAlarmReceiver.class.getName();
    }
}

Hinweis: Du musst den Wert BASE64_PUBLIC_KEY in den öffentlichen Schlüssel deines Publisher-Kontos ändern. Sie finden den Schlüssel in der Developer Console unter Ihren Profilinformationen. Dies ist auch beim Testen Ihrer Downloads erforderlich.

Denken Sie daran, den Dienst in Ihrer Manifestdatei zu deklarieren:

<app ...>
    <service android:name=".SampleDownloaderService" />
    ...
</app>

Implementierung des Alarmempfängers

Um den Fortschritt der Dateidownloads zu beobachten und den Download gegebenenfalls neu zu starten, plant DownloaderService einen RTC_WAKEUP-Alarm, der ein Intent an einen BroadcastReceiver in Ihrer Anwendung sendet. Sie müssen BroadcastReceiver definieren, um eine API aus der Downloader Library aufzurufen, die den Status des Downloads prüft und sie bei Bedarf neu startet.

Sie müssen lediglich die Methode onReceive() überschreiben, um DownloaderClientMarshaller.startDownloadServiceIfRequired() aufzurufen.

Beispiele:

Kotlin

class SampleAlarmReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        try {
            DownloaderClientMarshaller.startDownloadServiceIfRequired(
                    context,
                    intent,
                    SampleDownloaderService::class.java
            )
        } catch (e: PackageManager.NameNotFoundException) {
            e.printStackTrace()
        }
    }
}

Java

public class SampleAlarmReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        try {
            DownloaderClientMarshaller.startDownloadServiceIfRequired(context,
                intent, SampleDownloaderService.class);
        } catch (NameNotFoundException e) {
            e.printStackTrace();
        }
    }
}

Dies ist die Klasse, für die Sie den Namen in der Methode getAlarmReceiverClassName() des Dienstes zurückgeben müssen (siehe vorherigen Abschnitt).

Denke daran, den Empfänger in deiner Manifestdatei zu deklarieren:

<app ...>
    <receiver android:name=".SampleAlarmReceiver" />
    ...
</app>

Download wird gestartet

Die Hauptaktivität in deiner App (die durch das Launcher-Symbol gestartete) ist dafür verantwortlich, zu prüfen, ob sich die Erweiterungsdateien bereits auf dem Gerät befinden, und den Download zu starten, falls nicht.

So starten Sie den Download über die Downloader-Bibliothek:

  1. Prüfen Sie, ob die Dateien heruntergeladen wurden.

    Die Downloader Library enthält einige APIs in der Klasse Helper, die diesen Prozess erleichtern:

    • getExpansionAPKFileName(Context, c, boolean mainFile, int versionCode)
    • doesFileExist(Context c, String fileName, long fileSize)

    Die im APK-Erweiterungspaket bereitgestellte Beispiel-App ruft beispielsweise die folgende Methode in der Methode onCreate() der Aktivität auf, um zu prüfen, ob die Erweiterungsdateien bereits auf dem Gerät vorhanden sind:

    Kotlin

    fun expansionFilesDelivered(): Boolean {
        xAPKS.forEach { xf ->
            Helpers.getExpansionAPKFileName(this, xf.isBase, xf.fileVersion).also { fileName ->
                if (!Helpers.doesFileExist(this, fileName, xf.fileSize, false))
                    return false
            }
        }
        return true
    }
    

    Java

    boolean expansionFilesDelivered() {
        for (XAPKFile xf : xAPKS) {
            String fileName = Helpers.getExpansionAPKFileName(this, xf.isBase,
                xf.fileVersion);
            if (!Helpers.doesFileExist(this, fileName, xf.fileSize, false))
                return false;
        }
        return true;
    }
    

    In diesem Fall enthält jedes XAPKFile-Objekt die Versionsnummer und die Dateigröße einer bekannten Erweiterungsdatei sowie einen booleschen Wert, der angibt, ob es sich um die Haupterweiterungsdatei handelt. Weitere Informationen finden Sie in der Klasse SampleDownloaderActivity der Beispielanwendung.

    Wenn diese Methode false zurückgibt, muss die App den Download starten.

  2. Starten Sie den Download, indem Sie die statische Methode DownloaderClientMarshaller.startDownloadServiceIfRequired(Context c, PendingIntent notificationClient, Class<?> serviceClass) aufrufen.

    Die Methode nimmt folgende Parameter an:

    • context: Context Ihrer App.
    • notificationClient: Ein PendingIntent-Element zum Starten der Hauptaktivität. Dieser wird im Notification verwendet, das vom DownloaderService erstellt wird, um den Downloadfortschritt anzuzeigen. Wenn der Nutzer die Benachrichtigung auswählt, ruft das System die PendingIntent auf, die Sie hier angeben, und sollte die Aktivität öffnen, die den Downloadfortschritt anzeigt (normalerweise die Aktivität, mit der der Download gestartet wurde).
    • serviceClass: Das Class-Objekt für Ihre Implementierung von DownloaderService, das erforderlich ist, um den Dienst und gegebenenfalls den Download zu starten.

    Die Methode gibt eine Ganzzahl zurück, die angibt, ob der Download erforderlich ist. Mögliche Werte sind folgende:

    • NO_DOWNLOAD_REQUIRED: Wird zurückgegeben, wenn die Dateien bereits vorhanden sind oder bereits ein Download läuft.
    • LVL_CHECK_REQUIRED: Wird zurückgegeben, wenn eine Lizenzüberprüfung erforderlich ist, um die Erweiterungsdatei-URLs zu erhalten.
    • DOWNLOAD_REQUIRED: Wird zurückgegeben, wenn die URLs der Erweiterungsdateien bereits bekannt sind, aber nicht heruntergeladen wurden.

    Das Verhalten von LVL_CHECK_REQUIRED und DOWNLOAD_REQUIRED ist im Wesentlichen dasselbe und normalerweise brauchen Sie sich keine Gedanken darüber zu machen. In Ihrer Hauptaktivität, mit der startDownloadServiceIfRequired() aufgerufen wird, können Sie einfach prüfen, ob die Antwort NO_DOWNLOAD_REQUIRED ist. Wenn die Antwort anderer Wert als NO_DOWNLOAD_REQUIRED enthält, startet die Downloader-Bibliothek den Download und Sie sollten die Aktivitäts-UI so aktualisieren, dass der Downloadfortschritt angezeigt wird (siehe nächster Schritt). Wenn die Antwort NO_DOWNLOAD_REQUIRED lautet, sind die Dateien verfügbar und Ihre App kann gestartet werden.

    Beispiele:

    Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        // Check if expansion files are available before going any further
        if (!expansionFilesDelivered()) {
            val pendingIntent =
                    // Build an Intent to start this activity from the Notification
                    Intent(this, MainActivity::class.java).apply {
                        flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
                    }.let { notifierIntent ->
                        PendingIntent.getActivity(
                                this,
                                0,
                                notifierIntent,
                                PendingIntent.FLAG_UPDATE_CURRENT
                        )
                    }
    
    
            // Start the download service (if required)
            val startResult: Int = DownloaderClientMarshaller.startDownloadServiceIfRequired(
                    this,
                    pendingIntent,
                    SampleDownloaderService::class.java
            )
            // If download has started, initialize this activity to show
            // download progress
            if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
                // This is where you do set up to display the download
                // progress (next step)
                ...
                return
            } // If the download wasn't necessary, fall through to start the app
        }
        startApp() // Expansion files are available, start the app
    }
    

    Java

    @Override
    public void onCreate(Bundle savedInstanceState) {
        // Check if expansion files are available before going any further
        if (!expansionFilesDelivered()) {
            // Build an Intent to start this activity from the Notification
            Intent notifierIntent = new Intent(this, MainActivity.getClass());
            notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                                    Intent.FLAG_ACTIVITY_CLEAR_TOP);
            ...
            PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
                    notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    
            // Start the download service (if required)
            int startResult =
                DownloaderClientMarshaller.startDownloadServiceIfRequired(this,
                            pendingIntent, SampleDownloaderService.class);
            // If download has started, initialize this activity to show
            // download progress
            if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
                // This is where you do set up to display the download
                // progress (next step)
                ...
                return;
            } // If the download wasn't necessary, fall through to start the app
        }
        startApp(); // Expansion files are available, start the app
    }
    
  3. Wenn die Methode startDownloadServiceIfRequired() einen anderen Wert als NO_DOWNLOAD_REQUIRED zurückgibt, erstellen Sie durch Aufrufen von DownloaderClientMarshaller.CreateStub(IDownloaderClient client, Class<?> downloaderService) eine Instanz von IStub. Das IStub stellt eine Bindung zwischen Ihrer Aktivität an den Downloader-Dienst bereit, sodass Ihre Aktivität Callbacks zum Downloadfortschritt erhält.

    Um IStub durch Aufrufen von CreateStub() zu instanziieren, müssen Sie eine Implementierung der IDownloaderClient-Schnittstelle und Ihrer DownloaderService-Implementierung übergeben. Im nächsten Abschnitt zum Empfangen des Downloadfortschritts wird die IDownloaderClient-Schnittstelle erläutert, die Sie normalerweise in Ihrer Activity-Klasse implementieren sollten, damit Sie die Aktivitäts-UI aktualisieren können, wenn sich der Downloadstatus ändert.

    Wir empfehlen, CreateStub() aufzurufen, um IStub während der onCreate()-Methode deiner Aktivität zu instanziieren, nachdem startDownloadServiceIfRequired() den Download gestartet hat.

    Im vorherigen Codebeispiel für onCreate() können Sie beispielsweise so auf das Ergebnis startDownloadServiceIfRequired() antworten:

    Kotlin

            // Start the download service (if required)
            val startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(
                    this@MainActivity,
                    pendingIntent,
                    SampleDownloaderService::class.java
            )
            // If download has started, initialize activity to show progress
            if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
                // Instantiate a member instance of IStub
                downloaderClientStub =
                        DownloaderClientMarshaller.CreateStub(this, SampleDownloaderService::class.java)
                // Inflate layout that shows download progress
                setContentView(R.layout.downloader_ui)
                return
            }
    

    Java

            // Start the download service (if required)
            int startResult =
                DownloaderClientMarshaller.startDownloadServiceIfRequired(this,
                            pendingIntent, SampleDownloaderService.class);
            // If download has started, initialize activity to show progress
            if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
                // Instantiate a member instance of IStub
                downloaderClientStub = DownloaderClientMarshaller.CreateStub(this,
                        SampleDownloaderService.class);
                // Inflate layout that shows download progress
                setContentView(R.layout.downloader_ui);
                return;
            }
    

    Nachdem die Methode onCreate() zurückgegeben wurde, empfängt Ihre Aktivität einen Aufruf an onResume(). Hier sollten Sie dann connect() für IStub aufrufen und die Context Ihrer App übergeben. Umgekehrt sollten Sie disconnect() im onStop()-Callback Ihrer Aktivität aufrufen.

    Kotlin

    override fun onResume() {
        downloaderClientStub?.connect(this)
        super.onResume()
    }
    
    override fun onStop() {
        downloaderClientStub?.disconnect(this)
        super.onStop()
    }
    

    Java

    @Override
    protected void onResume() {
        if (null != downloaderClientStub) {
            downloaderClientStub.connect(this);
        }
        super.onResume();
    }
    
    @Override
    protected void onStop() {
        if (null != downloaderClientStub) {
            downloaderClientStub.disconnect(this);
        }
        super.onStop();
    }
    

    Durch das Aufrufen von connect() im IStub wird Ihre Aktivität an das DownloaderService gebunden, sodass Ihre Aktivität Callbacks zu Änderungen des Downloadstatus über die IDownloaderClient-Schnittstelle erhält.

Downloadfortschritt wird empfangen

Damit du Updates zum Downloadfortschritt erhalten und mit DownloaderService interagieren kannst, musst du die IDownloaderClient-Schnittstelle der Downloader-Bibliothek implementieren. In der Regel sollte mit der Aktivität, die Sie zum Starten des Downloads verwenden, diese Schnittstelle implementiert werden, um den Downloadfortschritt anzuzeigen und Anfragen an den Dienst zu senden.

Die erforderlichen Schnittstellenmethoden für IDownloaderClient sind:

onServiceConnected(Messenger m)
Nachdem Sie IStub in Ihrer Aktivität instanziiert haben, erhalten Sie einen Aufruf dieser Methode, der ein Messenger-Objekt übergibt, das mit Ihrer Instanz von DownloaderService verbunden ist. Wenn Sie Anfragen an den Dienst senden möchten, beispielsweise um Downloads anzuhalten oder fortzusetzen, müssen Sie DownloaderServiceMarshaller.CreateProxy() aufrufen, um die mit dem Dienst verbundene IDownloaderService-Schnittstelle zu empfangen.

Eine empfohlene Implementierung sieht so aus:

Kotlin

private var remoteService: IDownloaderService? = null
...

override fun onServiceConnected(m: Messenger) {
    remoteService = DownloaderServiceMarshaller.CreateProxy(m).apply {
        downloaderClientStub?.messenger?.also { messenger ->
            onClientUpdated(messenger)
        }
    }
}

Java

private IDownloaderService remoteService;
...

@Override
public void onServiceConnected(Messenger m) {
    remoteService = DownloaderServiceMarshaller.CreateProxy(m);
    remoteService.onClientUpdated(downloaderClientStub.getMessenger());
}

Wenn das IDownloaderService-Objekt initialisiert ist, können Sie Befehle an den Downloader-Dienst senden, z. B. zum Anhalten und Fortsetzen des Downloads (requestPauseDownload() und requestContinueDownload()).

onDownloadStateChanged(int newState)
Der Downloaddienst ruft ihn auf, wenn eine Änderung des Downloadstatus auftritt, z. B. wenn der Download beginnt oder abgeschlossen wird.

Der Wert newState ist einer von mehreren möglichen Werten, die durch eine der STATE_*-Konstanten der IDownloaderClient-Klasse angegeben werden.

Damit Ihre Nutzer eine nützliche Nachricht erhalten, können Sie durch Aufrufen von Helpers.getDownloaderStringResourceIDFromState() einen entsprechenden String für jeden Status anfordern. Dadurch wird die Ressourcen-ID für einen der Strings zurückgegeben, die mit der Downloader-Bibliothek gebündelt sind. Der String „Download angehalten, weil du Roaming nutzt“ entspricht beispielsweise STATE_PAUSED_ROAMING.

onDownloadProgress(DownloadProgressInfo progress)
Der Downloaddienst ruft dies auf, um ein DownloadProgressInfo-Objekt bereitzustellen, das verschiedene Informationen zum Downloadfortschritt beschreibt, einschließlich der voraussichtlichen verbleibenden Zeit, der aktuellen Geschwindigkeit, des Gesamtfortschritts und des Gesamtwerts, sodass Sie die Benutzeroberfläche für den Downloadfortschritt aktualisieren können.

Tipp:Beispiele für diese Callbacks, mit denen die Benutzeroberfläche für den Downloadfortschritt aktualisiert wird, finden Sie unter SampleDownloaderActivity in der Beispiel-App, die mit dem APK-Erweiterungspaket bereitgestellt wird.

Einige öffentliche Methoden für die IDownloaderService-Schnittstelle, die Sie nützlich finden könnten, sind:

requestPauseDownload()
Der Download wird angehalten.
requestContinueDownload()
Setzt einen angehaltenen Download fort.
setDownloadFlags(int flags)
Hiermit werden Nutzereinstellungen für Netzwerktypen festgelegt, auf die Dateien heruntergeladen werden können. Die aktuelle Implementierung unterstützt nur das Flag FLAGS_DOWNLOAD_OVER_CELLULAR. Sie können aber noch weitere hinzufügen. Dieses Flag ist standardmäßig nicht aktiviert. Der Nutzer muss also mit einem WLAN verbunden sein, um Erweiterungsdateien herunterladen zu können. Sie können eine Nutzereinstellung festlegen, die Downloads über das Mobilfunknetz ermöglicht. In diesem Fall können Sie Folgendes aufrufen:

Kotlin

remoteService = DownloaderServiceMarshaller.CreateProxy(m).apply {
    ...
    setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR)
}

Java

remoteService
    .setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);

APKExpansionPolicy wird verwendet

Wenn du einen eigenen Downloader-Dienst erstellen möchtest, statt die Downloader-Bibliothek von Google Play zu verwenden, solltest du trotzdem die APKExpansionPolicy aus der Bibliothek zur Lizenzüberprüfung verwenden. Die Klasse APKExpansionPolicy ist nahezu identisch mit ServerManagedPolicy (verfügbar in der Bibliothek zur Lizenzüberprüfung von Google Play), umfasst aber zusätzliche Verarbeitungsvorgänge für die Extras der APK-Erweiterungsdateiantwort.

Hinweis:Wenn Sie die Downloader Library wie im vorherigen Abschnitt beschrieben verwenden, führt die Bibliothek alle Interaktionen mit dem APKExpansionPolicy aus, sodass Sie diese Klasse nicht direkt verwenden müssen.

Die Klasse enthält Methoden, mit denen Sie die erforderlichen Informationen zu den verfügbaren Erweiterungsdateien abrufen können:

  • getExpansionURLCount()
  • getExpansionURL(int index)
  • getExpansionFileName(int index)
  • getExpansionFileSize(int index)

Weitere Informationen zur Verwendung der APKExpansionPolicy, wenn Sie nicht die Downloader-Bibliothek verwenden, finden Sie in der Dokumentation zu Das Hinzufügen einer solchen Richtlinie zu Ihrer App.Das Hinzufügen einer solchen Richtlinie zu Ihrer App<br class="

Erweiterungsdatei lesen

Sobald Ihre APK-Erweiterungsdateien auf dem Gerät gespeichert sind, hängt das Lesen der Dateien vom verwendeten Dateityp ab. Wie in der Übersicht beschrieben, können Sie die Erweiterungsdateien aus jedem beliebigen Format erstellen. Sie werden jedoch in einem bestimmten Dateinamenformat umbenannt und im <shared-storage>/Android/obb/<package-name>/ gespeichert.

Unabhängig davon, wie Sie Ihre Dateien lesen, sollten Sie immer zuerst prüfen, ob der externe Speicher zum Lesen verfügbar ist. Es besteht die Möglichkeit, dass der Nutzer den Speicher über USB auf einem Computer bereitgestellt oder die SD-Karte tatsächlich entfernt hat.

Hinweis:Wenn Ihre Anwendung gestartet wird, sollten Sie immer prüfen, ob der externe Speicherplatz verfügbar und lesbar ist. Rufen Sie dazu getExternalStorageState() auf. Dadurch wird einer von mehreren möglichen Strings zurückgegeben, die den Status des externen Speichers darstellen. Damit sie von Ihrer Anwendung gelesen werden kann, muss der Rückgabewert MEDIA_MOUNTED sein.

Dateinamen abrufen

Wie in der Übersicht beschrieben, werden Ihre APK-Erweiterungsdateien mit einem bestimmten Dateinamensformat gespeichert:

[main|patch].<expansion-version>.<package-name>.obb

Verwenden Sie zum Ermitteln des Speicherorts und der Namen Ihrer Erweiterungsdateien die Methoden getExternalStorageDirectory() und getPackageName(), um den Pfad zu Ihren Dateien zu erstellen.

Hier ist eine Methode, die Sie in Ihrer App verwenden können, um ein Array mit dem vollständigen Pfad zu Ihren beiden Erweiterungsdateien zu erhalten:

Kotlin

fun getAPKExpansionFiles(ctx: Context, mainVersion: Int, patchVersion: Int): Array<String> {
    val packageName = ctx.packageName
    val ret = mutableListOf<String>()
    if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
        // Build the full path to the app's expansion files
        val root = Environment.getExternalStorageDirectory()
        val expPath = File(root.toString() + EXP_PATH + packageName)

        // Check that expansion file path exists
        if (expPath.exists()) {
            if (mainVersion > 0) {
                val strMainPath = "$expPath${File.separator}main.$mainVersion.$packageName.obb"
                val main = File(strMainPath)
                if (main.isFile) {
                    ret += strMainPath
                }
            }
            if (patchVersion > 0) {
                val strPatchPath = "$expPath${File.separator}patch.$mainVersion.$packageName.obb"
                val main = File(strPatchPath)
                if (main.isFile) {
                    ret += strPatchPath
                }
            }
        }
    }
    return ret.toTypedArray()
}

Java

// The shared path to all app expansion files
private final static String EXP_PATH = "/Android/obb/";

static String[] getAPKExpansionFiles(Context ctx, int mainVersion,
      int patchVersion) {
    String packageName = ctx.getPackageName();
    Vector<String> ret = new Vector<String>();
    if (Environment.getExternalStorageState()
          .equals(Environment.MEDIA_MOUNTED)) {
        // Build the full path to the app's expansion files
        File root = Environment.getExternalStorageDirectory();
        File expPath = new File(root.toString() + EXP_PATH + packageName);

        // Check that expansion file path exists
        if (expPath.exists()) {
            if ( mainVersion > 0 ) {
                String strMainPath = expPath + File.separator + "main." +
                        mainVersion + "." + packageName + ".obb";
                File main = new File(strMainPath);
                if ( main.isFile() ) {
                        ret.add(strMainPath);
                }
            }
            if ( patchVersion > 0 ) {
                String strPatchPath = expPath + File.separator + "patch." +
                        mainVersion + "." + packageName + ".obb";
                File main = new File(strPatchPath);
                if ( main.isFile() ) {
                        ret.add(strPatchPath);
                }
            }
        }
    }
    String[] retArray = new String[ret.size()];
    ret.toArray(retArray);
    return retArray;
}

Sie können diese Methode aufrufen, indem Sie die Context der App und die Version der gewünschten Erweiterungsdatei übergeben.

Es gibt viele Möglichkeiten, die Versionsnummer der Erweiterungsdatei zu bestimmen. Eine einfache Möglichkeit besteht darin, die Version zu Beginn des Downloads in einer SharedPreferences-Datei zu speichern. Dazu fragen Sie den Namen der Erweiterungsdatei mit der Methode getExpansionFileName(int index) der APKExpansionPolicy-Klasse ab. Sie können dann den Versionscode abrufen, indem Sie die Datei SharedPreferences lesen, wenn Sie auf die Erweiterungsdatei zugreifen möchten.

Weitere Informationen zum Lesen aus dem freigegebenen Speicher finden Sie in der Dokumentation zum Datenspeicher.

APK-Erweiterungs-ZIP-Bibliothek verwenden

Das APK-Erweiterungspaket von Google Market enthält eine Bibliothek namens APK-Erweiterungs-Zip-Bibliothek (unter <sdk>/extras/google/google_market_apk_expansion/zip_file/). Hierbei handelt es sich um eine optionale Bibliothek, mit der Sie Ihre als ZIP-Dateien gespeicherten Erweiterungsdateien lesen können. Mit dieser Bibliothek können Sie Ressourcen aus Ihren ZIP-Erweiterungsdateien ganz einfach als virtuelles Dateisystem lesen.

Die ZIP-Bibliothek für die APK-Erweiterung enthält die folgenden Klassen und APIs:

APKExpansionSupport
Bietet verschiedene Methoden für den Zugriff auf Erweiterungsdateinamen und ZIP-Dateien:
getAPKExpansionFiles()
Die oben gezeigte Methode, mit der der vollständige Dateipfad zu beiden Erweiterungsdateien zurückgegeben wird.
getAPKExpansionZipFile(Context ctx, int mainVersion, int patchVersion)
Gibt einen ZipResourceFile-Wert zurück, der die Summe der Hauptdatei und der Patchdatei darstellt. Wenn du also sowohl mainVersion als auch patchVersion angibst, wird ein ZipResourceFile zurückgegeben, das Lesezugriff auf alle Daten bietet. Die Daten der Patchdatei werden über der Hauptdatei zusammengeführt.
ZipResourceFile
Stellt eine ZIP-Datei im freigegebenen Speicher dar und führt alle Schritte zur Bereitstellung eines virtuellen Dateisystems auf der Grundlage Ihrer ZIP-Dateien aus. Sie können eine Instanz mit APKExpansionSupport.getAPKExpansionZipFile() oder mit ZipResourceFile abrufen. Übergeben Sie dazu den Pfad an Ihre Erweiterungsdatei. Diese Klasse enthält eine Vielzahl nützlicher Methoden, auf die meisten müssen Sie jedoch nicht zugreifen. Hier einige wichtige Methoden:
getInputStream(String assetPath)
Stellt ein InputStream-Element bereit, mit dem eine in der ZIP-Datei enthaltene Datei gelesen werden kann. Der assetPath muss der Pfad zur gewünschten Datei sein und sich auf das Stammverzeichnis der ZIP-Datei beziehen.
getAssetFileDescriptor(String assetPath)
Stellt ein AssetFileDescriptor für eine Datei in der ZIP-Datei bereit. Der assetPath muss der Pfad zur gewünschten Datei sein und sich auf das Stammverzeichnis der ZIP-Datei beziehen. Das ist nützlich für bestimmte Android APIs, die ein AssetFileDescriptor erfordern, z. B. einige MediaPlayer APIs.
APEZProvider
Für die meisten Apps ist dieser Kurs nicht erforderlich. Diese Klasse definiert eine ContentProvider, die die Daten aus den ZIP-Dateien über den Uri des Contentanbieters abruft, um bestimmten Android APIs Dateizugriff zu ermöglichen, die Uri-Zugriff auf Mediendateien erwarten. Das ist beispielsweise nützlich, wenn Sie ein Video mit VideoView.setVideoURI() abspielen möchten.

ZIP-Komprimierung von Mediendateien wird übersprungen

Wenn Sie Erweiterungsdateien zum Speichern von Mediendateien verwenden, können Sie mithilfe einer ZIP-Datei weiterhin Android-Medienwiedergabeaufrufe verwenden, die Offset- und Längensteuerelemente wie MediaPlayer.setDataSource() und SoundPool.load() bereitstellen. Damit dies funktioniert, dürfen die Mediendateien beim Erstellen der ZIP-Pakete nicht zusätzlich komprimiert werden. Wenn Sie beispielsweise das zip-Tool verwenden, sollten Sie die Option -n verwenden, um die Dateisuffixe anzugeben, die nicht komprimiert werden sollen:

zip -n .mp4;.ogg main_expansion media_files

Aus einer ZIP-Datei lesen

Wenn Sie die APK-Erweiterungs-ZIP-Bibliothek verwenden, müssen Sie normalerweise Folgendes tun, um eine Datei aus der ZIP-Datei zu lesen:

Kotlin

// Get a ZipResourceFile representing a merger of both the main and patch files
val expansionFile =
        APKExpansionSupport.getAPKExpansionZipFile(appContext, mainVersion, patchVersion)

// Get an input stream for a known file inside the expansion file ZIPs
expansionFile.getInputStream(pathToFileInsideZip).use {
    ...
}

Java

// Get a ZipResourceFile representing a merger of both the main and patch files
ZipResourceFile expansionFile =
    APKExpansionSupport.getAPKExpansionZipFile(appContext,
        mainVersion, patchVersion);

// Get an input stream for a known file inside the expansion file ZIPs
InputStream fileStream = expansionFile.getInputStream(pathToFileInsideZip);

Der obige Code bietet Zugriff auf jede Datei, die entweder in Ihrer Haupt- oder Patcherweiterungsdatei vorhanden ist, indem er aus einer zusammengeführten Karte aller Dateien aus beiden Dateien ausliest. Sie müssen für die Methode getAPKExpansionFile() lediglich Ihre App-android.content.Context und die Versionsnummer sowohl für die Haupterweiterungsdatei als auch für die Patch-Erweiterungsdatei angeben.

Wenn Sie lieber aus einer bestimmten Erweiterungsdatei lesen möchten, können Sie den Konstruktor ZipResourceFile mit dem Pfad zur gewünschten Erweiterungsdatei verwenden:

Kotlin

// Get a ZipResourceFile representing a specific expansion file
val expansionFile = ZipResourceFile(filePathToMyZip)

// Get an input stream for a known file inside the expansion file ZIPs
expansionFile.getInputStream(pathToFileInsideZip).use {
    ...
}

Java

// Get a ZipResourceFile representing a specific expansion file
ZipResourceFile expansionFile = new ZipResourceFile(filePathToMyZip);

// Get an input stream for a known file inside the expansion file ZIPs
InputStream fileStream = expansionFile.getInputStream(pathToFileInsideZip);

Weitere Informationen zur Verwendung dieser Bibliothek für Ihre Erweiterungsdateien finden Sie in der Klasse SampleDownloaderActivity der Beispielanwendung. Sie enthält zusätzlichen Code zum Überprüfen der heruntergeladenen Dateien mit CRC. Wenn Sie dieses Beispiel als Grundlage für Ihre eigene Implementierung verwenden, müssen Sie die Bytegröße Ihrer Erweiterungsdateien im Array xAPKS deklarieren.

Erweiterungsdateien testen

Bevor Sie Ihre Anwendung veröffentlichen, sollten Sie zwei Dinge testen: Lesen Sie die Erweiterungsdateien und laden Sie die Dateien herunter.

Dateilesevorgänge testen

Bevor Sie Ihre App bei Google Play hochladen, sollten Sie testen, ob sie die Dateien aus dem freigegebenen Speicher lesen kann. Sie müssen lediglich die Dateien am entsprechenden Speicherort im freigegebenen Speicher des Geräts hinzufügen und die App starten:

  1. Erstellen Sie auf Ihrem Gerät das entsprechende Verzeichnis im freigegebenen Speicher, in dem Google Play Ihre Dateien speichert.

    Wenn Ihr Paketname beispielsweise com.example.android lautet, müssen Sie das Verzeichnis Android/obb/com.example.android/ im freigegebenen Speicherplatz erstellen. Schließen Sie das Testgerät an Ihren Computer an, um den freigegebenen Speicher bereitzustellen, und erstellen Sie dieses Verzeichnis manuell.

  2. Fügen Sie die Erweiterungsdateien manuell zu diesem Verzeichnis hinzu. Achten Sie darauf, Ihre Dateien so umzubenennen, dass sie dem von Google Play verwendeten Dateinamenformat entsprechen.

    Beispielsweise sollte die Haupterweiterungsdatei für die com.example.android-App unabhängig vom Dateityp main.0300110.com.example.android.obb sein. Der Versionscode kann ein beliebiger Wert sein. Denk daran:

    • Die Haupterweiterungsdatei beginnt immer mit main und die Patchdatei mit patch.
    • Der Paketname stimmt immer mit dem Namen des APKs überein, an das die Datei bei Google Play angehängt ist.
  3. Nachdem sich die Erweiterungsdatei(en) auf dem Gerät befindet, können Sie die App installieren und ausführen, um Ihre Erweiterungsdateien zu testen.

Hier sind einige Hinweise zum Umgang mit Erweiterungsdateien:

  • Löschen oder benennen Sie die .obb-Erweiterungsdateien nicht um, auch wenn Sie die Daten an einem anderen Speicherort entpacken. Dies führt dazu, dass Google Play (oder Ihre App selbst) die Erweiterungsdatei wiederholt herunterlädt.
  • Speichern Sie keine anderen Daten im Verzeichnis obb/. Wenn du Daten entpacken musst, speichere sie an dem in getExternalFilesDir() angegebenen Speicherort.

Dateidownloads testen

Da Ihre Anwendung die Erweiterungsdateien beim ersten Öffnen manchmal manuell herunterladen muss, ist es wichtig, dass Sie diesen Vorgang testen, um sicherzustellen, dass Ihre Anwendung die URLs erfolgreich abfragen, die Dateien herunterladen und auf dem Gerät speichern kann.

Wenn du die Implementierung des manuellen Downloadvorgangs in deiner App testen möchtest, kannst du sie im internen Test-Track veröffentlichen, damit sie nur autorisierten Testern zur Verfügung steht. Wenn alles wie erwartet funktioniert, sollte Ihre App mit dem Herunterladen der Erweiterungsdateien beginnen, sobald die Hauptaktivität beginnt.

Hinweis:Bisher konnten Sie eine Anwendung testen, indem Sie eine unveröffentlichte Entwurfsversion hochgeladen haben. Diese Funktion wird nicht mehr unterstützt. Stattdessen musst du sie in einem internen, geschlossenen oder offenen Test-Track veröffentlichen. Weitere Informationen finden Sie unter App-Entwürfe werden nicht mehr unterstützt.

App aktualisieren

Einer der großen Vorteile von Erweiterungsdateien bei Google Play ist die Möglichkeit, Ihre App zu aktualisieren, ohne alle ursprünglichen Assets erneut herunterladen zu müssen. Da Sie in Google Play mit jedem APK zwei Erweiterungsdateien bereitstellen können, können Sie die zweite Datei als "Patch" verwenden, das Updates und neue Assets bereitstellt. Auf diese Weise müssen Sie die Haupterweiterungsdatei nicht noch einmal herunterladen, da dies für Nutzer groß und teuer sein könnte.

Die Patcherweiterungsdatei ist technisch gesehen mit der Haupterweiterungsdatei identisch und weder das Android-System noch Google Play führen tatsächliche Patches zwischen Ihren Haupt- und Patcherweiterungsdateien durch. Der App-Code muss alle erforderlichen Patches selbst ausführen.

Wenn Sie ZIP-Dateien als Erweiterungsdateien verwenden, können Sie in der ZIP-Erweiterungsbibliothek für APKs, die im APK-Erweiterungspaket enthalten ist, Ihre Patchdatei mit der Haupterweiterungsdatei zusammenführen.

Hinweis:Auch wenn Sie nur Änderungen an der Patch-Erweiterungsdatei vornehmen müssen, müssen Sie das APK aktualisieren, damit Google Play ein Update durchführen kann. Wenn keine Codeänderungen in der App erforderlich sind, aktualisieren Sie einfach versionCode im Manifest.

Solange du die Haupterweiterungsdatei, die mit dem APK in der Play Console verknüpft ist, nicht änderst, laden Nutzer, die deine App bereits installiert haben, die Haupterweiterungsdatei nicht herunter. Bestehende Nutzer erhalten nur das aktualisierte APK und die neue Patcherweiterungsdatei unter Beibehaltung der vorherigen Haupterweiterungsdatei.

Im Zusammenhang mit Aktualisierungen von Erweiterungsdateien ist Folgendes zu beachten:

  • Für Ihre App können immer nur zwei Erweiterungsdateien gleichzeitig vorhanden sein. Eine Haupt-Erweiterungsdatei und eine Patch-Erweiterungsdatei. Während der Aktualisierung einer Datei löscht Google Play die vorherige Version. Dies gilt auch für die App, wenn manuelle Updates durchgeführt werden.
  • Wenn Sie eine Patch-Erweiterungsdatei hinzufügen, patcht das Android-System Ihre App oder Haupt-Erweiterungsdatei nicht tatsächlich. Du musst deine App so konzipieren, dass sie die Patchdaten unterstützt. Das APK-Erweiterungspaket enthält jedoch eine Bibliothek zur Verwendung von ZIP-Dateien als Erweiterungsdateien. Darin werden die Daten aus der Patchdatei in die Haupterweiterungsdatei zusammengeführt, sodass Sie alle Daten der Erweiterungsdatei einfach lesen können.