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
oderpatch
- 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>/
<shared-storage>
ist der Pfad zum freigegebenen Speicherplatz, der übergetExternalStorageDirectory()
verfügbar ist.<package-name>
ist der Paketname Ihrer Anwendung im Java-Stil, der ingetPackageName()
verfügbar ist.
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:
- Der Nutzer installiert deine App über Google Play.
- 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.
- Wenn der Nutzer deine App startet, muss sie überprüfen, ob die Erweiterungsdateien bereits auf dem Gerät gespeichert sind.
- Falls ja, ist Ihre App einsatzbereit.
- 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:
- 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.
- 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.
- 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.
- 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:
- Eine Erweiterungsdatei darf nicht größer als 2 GB sein.
- 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.
- 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.
- 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.
- 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. - 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 auchversionName
ändern. - Speichern Sie keine anderen Daten im Verzeichnis
obb/
. Wenn du Daten entpacken musst, speichere sie an dem ingetExternalFilesDir()
angegebenen Speicherort. - 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. - 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:
- Suchen Sie beim Start der App nach den Erweiterungsdateien am freigegebenen Speicherort (im Verzeichnis
Android/obb/<package-name>/
).- Wenn die Erweiterungsdateien bereits vorhanden sind, sind keine weiteren Maßnahmen erforderlich und Ihre App kann fortfahren.
- Wenn die Erweiterungsdateien nicht vorhanden sind:
- Stellen Sie eine Anfrage über die App-Lizenzierung von Google Play, um die Namen, Größen und URLs der Erweiterungsdateien Ihrer App zu erhalten.
- 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 abgeleiteteBroadcastReceiver
-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:
- Wählen Sie File > New > New Module aus.
- Wählen Sie im Fenster Create New Module (Neues Modul erstellen) die Option Android Library (Android-Bibliothek) und dann Next (Weiter) aus.
- 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.
- Wählen Sie File > Project Structure (Datei > Projektstruktur) aus.
- 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 oderplay_apk_expansion/downloader_library/
für die Downloader-Bibliothek). - 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:
- Wechseln Sie in das Verzeichnis
<sdk>/tools/
. - 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 (dieCONNECTIVITY_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 einerObfuscator
verwendet werden. So ist sichergestellt, dass die verschleierteSharedPreferences
-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:
- 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 KlasseSampleDownloaderActivity
der Beispielanwendung.Wenn diese Methode false zurückgibt, muss die App den Download starten.
- 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
: EinPendingIntent
-Element zum Starten der Hauptaktivität. Dieser wird imNotification
verwendet, das vomDownloaderService
erstellt wird, um den Downloadfortschritt anzuzeigen. Wenn der Nutzer die Benachrichtigung auswählt, ruft das System diePendingIntent
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
: DasClass
-Objekt für Ihre Implementierung vonDownloaderService
, 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
undDOWNLOAD_REQUIRED
ist im Wesentlichen dasselbe und normalerweise brauchen Sie sich keine Gedanken darüber zu machen. In Ihrer Hauptaktivität, mit derstartDownloadServiceIfRequired()
aufgerufen wird, können Sie einfach prüfen, ob die AntwortNO_DOWNLOAD_REQUIRED
ist. Wenn die Antwort anderer Wert alsNO_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 AntwortNO_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 }
- Wenn die Methode
startDownloadServiceIfRequired()
einen anderen Wert alsNO_DOWNLOAD_REQUIRED
zurückgibt, erstellen Sie durch Aufrufen vonDownloaderClientMarshaller.CreateStub(IDownloaderClient client, Class<?> downloaderService)
eine Instanz vonIStub
. DasIStub
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 vonCreateStub()
zu instanziieren, müssen Sie eine Implementierung derIDownloaderClient
-Schnittstelle und IhrerDownloaderService
-Implementierung übergeben. Im nächsten Abschnitt zum Empfangen des Downloadfortschritts wird dieIDownloaderClient
-Schnittstelle erläutert, die Sie normalerweise in IhrerActivity
-Klasse implementieren sollten, damit Sie die Aktivitäts-UI aktualisieren können, wenn sich der Downloadstatus ändert.Wir empfehlen,
CreateStub()
aufzurufen, umIStub
während deronCreate()
-Methode deiner Aktivität zu instanziieren, nachdemstartDownloadServiceIfRequired()
den Download gestartet hat.Im vorherigen Codebeispiel für
onCreate()
können Sie beispielsweise so auf das ErgebnisstartDownloadServiceIfRequired()
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 anonResume()
. Hier sollten Sie dannconnect()
fürIStub
aufrufen und dieContext
Ihrer App übergeben. Umgekehrt sollten Siedisconnect()
imonStop()
-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()
imIStub
wird Ihre Aktivität an dasDownloaderService
gebunden, sodass Ihre Aktivität Callbacks zu Änderungen des Downloadstatus über dieIDownloaderClient
-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 einMessenger
-Objekt übergibt, das mit Ihrer Instanz vonDownloaderService
verbunden ist. Wenn Sie Anfragen an den Dienst senden möchten, beispielsweise um Downloads anzuhalten oder fortzusetzen, müssen SieDownloaderServiceMarshaller.CreateProxy()
aufrufen, um die mit dem Dienst verbundeneIDownloaderService
-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()
undrequestContinueDownload()
). 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 derSTATE_*
-Konstanten derIDownloaderClient
-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 beispielsweiseSTATE_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 sowohlmainVersion
als auchpatchVersion
angibst, wird einZipResourceFile
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 mitZipResourceFile
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. DerassetPath
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. DerassetPath
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 einAssetFileDescriptor
erfordern, z. B. einigeMediaPlayer
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 denUri
des Contentanbieters abruft, um bestimmten Android APIs Dateizugriff zu ermöglichen, dieUri
-Zugriff auf Mediendateien erwarten. Das ist beispielsweise nützlich, wenn Sie ein Video mitVideoView.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:
- 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 VerzeichnisAndroid/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. - 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 Dateitypmain.0300110.com.example.android.obb
sein. Der Versionscode kann ein beliebiger Wert sein. Denk daran:- Die Haupterweiterungsdatei beginnt immer mit
main
und die Patchdatei mitpatch
. - Der Paketname stimmt immer mit dem Namen des APKs überein, an das die Datei bei Google Play angehängt ist.
- Die Haupterweiterungsdatei beginnt immer mit
- 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 ingetExternalFilesDir()
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.