Bei Google Play darf die komprimierte APK-Datei, die Nutzer herunterladen, maximal 100 MB groß sein. Für die meisten Apps ist das ausreichend Platz für den gesamten Code und alle Assets der App. Einige Apps benötigen jedoch mehr Speicherplatz für hochauflösende Grafiken, Mediendateien oder andere große Assets. Bisher mussten Sie zusätzliche Ressourcen selbst hosten und herunterladen, wenn die komprimierte Downloadgröße Ihrer App 100 MB überschritt. Das Hosten und Bereitstellen der zusätzlichen Dateien kann kostspielig sein und die Nutzerfreundlichkeit ist oft nicht optimal. Um diesen Prozess für Sie zu vereinfachen und für 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 im produktübergreifenden Speicher des Geräts gespeichert (auf der SD-Karte oder USB-Partition, auch als „externer“ Speicher bezeichnet), wo Ihre App auf sie zugreifen kann. Auf den meisten Geräten lädt Google Play die Erweiterungsdatei(en) gleichzeitig mit dem APK herunter, sodass Ihre App alles hat, was sie benötigt, wenn der Nutzer sie zum ersten Mal öffnet. In einigen Fällen muss Ihre App die Dateien jedoch beim Start aus Google Play herunterladen.
Wenn Sie keine Erweiterungsdateien verwenden möchten und die komprimierte Downloadgröße Ihrer App größer als 100 MB ist, sollten Sie Ihre App stattdessen mit Android-App-Bundles hochladen. Diese ermöglichen eine komprimierte Downloadgröße von bis zu 500 MB. Da die APK-Generierung und ‑Signierung bei Verwendung von App-Bundles an Google Play übertragen werden, laden Nutzer außerdem optimierte APKs mit nur dem Code und den Ressourcen herunter, die sie zum Ausführen Ihrer App benötigen. Sie müssen 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, eine komprimierte Datei zu verwenden, um beim Download Bandbreite zu sparen. Konzeptionell spielt jede Erweiterungsdatei eine andere Rolle:
- Die Haupterweiterungsdatei 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 Updates der Haupterweiterungsdatei vorgesehen.
Sie können die beiden Erweiterungsdateien beliebig verwenden. Wir empfehlen jedoch, die primären Assets in der Haupterweiterungsdatei bereitzustellen und diese nur selten oder gar nicht zu aktualisieren. Die Patch-Erweiterungsdatei sollte kleiner sein und als „Patch-Carrier“ dienen. Sie wird mit jeder Hauptversion oder nach Bedarf aktualisiert.
Auch wenn für Ihr App-Update nur eine neue Patch-Erweiterungsdatei erforderlich ist, müssen Sie ein neues APK mit einem aktualisierten versionCode im Manifest hochladen. (In der Play Console können Sie keine Erweiterungsdatei in ein vorhandenes APK hochladen.)
Hinweis:Die Patch-Erweiterungsdatei ist semantisch identisch mit der Haupt-Erweiterungsdatei. Sie können jede Datei beliebig verwenden.
Dateinamenformat
Jede Erweiterungsdatei, die Sie hochladen, kann ein beliebiges Format haben (ZIP, PDF, MP4 usw.). Sie können auch das Tool JOBB verwenden, um eine Reihe von Ressourcendateien und nachfolgende Patches für diese Dateien zu kapseln und zu verschlüsseln. Unabhängig vom Dateityp betrachtet Google Play sie als undurchsichtige binäre Blobs und benennt die Dateien nach folgendem Schema um:
[main|patch].<expansion-version>.<package-name>.obb
Das Schema besteht aus drei Komponenten:
mainoderpatch- 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>- Dies ist eine Ganzzahl, die dem Versionscode des APK entspricht, dem die Erweiterung zuerst zugeordnet wurde (sie entspricht dem Wert
android:versionCodeder App).„Erste“ wird deshalb betont, weil Sie zwar in der Play Console eine hochgeladene Erweiterungsdatei mit einem neuen APK wiederverwenden können, der Name der Erweiterungsdatei sich jedoch nicht ändert. Er behält die Version bei, die beim ersten Hochladen der Datei angewendet wurde.
<package-name>- Der Paketname Ihrer App im Java-Stil.
Angenommen, Ihre APK-Version ist 314159 und Ihr Paketname ist com.example.app. Wenn Sie eine Haupt-Erweiterungsdatei hochladen, wird die Datei umbenannt in:
main.314159.com.example.app.obb
Storage-Speicherort
Wenn Google Play die Erweiterungsdateien auf ein Gerät herunterlädt, werden sie im freigegebenen Speicherort des Systems gespeichert. Damit die Erweiterungsdateien ordnungsgemäß funktionieren, dürfen Sie sie nicht löschen, verschieben oder umbenennen. Falls Ihre App den Download selbst über Google Play ausführen muss, müssen Sie die Dateien am selben Speicherort speichern.
Die Methode getObbDir() gibt den spezifischen Speicherort für Ihre Erweiterungsdateien in folgender Form zurück:
<shared-storage>/Android/obb/<package-name>/
<shared-storage>ist der Pfad zum gemeinsamen Speicherplatz, der übergetExternalStorageDirectory()verfügbar ist.<package-name>ist der Paketname Ihrer App im Java-Stil, der untergetPackageName()verfügbar ist.
Für jede App sind in diesem Verzeichnis nie mehr als zwei Erweiterungsdateien vorhanden.
Eine ist die Haupterweiterungsdatei 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 die Berechtigung für externen Speicher lesen. Bei einigen Implementierungen von Android 6.0 (API-Level 23) und höher ist jedoch weiterhin eine Berechtigung erforderlich. Sie müssen also die Berechtigung READ_EXTERNAL_STORAGE im App-Manifest deklarieren und zur Laufzeit wie folgt um die Berechtigung bitten:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Bei Android-Version 6 und höher muss die Berechtigung für externen Speicher zur Laufzeit angefordert werden. Bei einigen Android-Implementierungen ist jedoch keine Berechtigung zum Lesen von OBB-Dateien erforderlich. Das folgende Code-Snippet zeigt, wie Sie vor dem Anfordern der Berechtigung für den externen Speicher prüfen, ob Lesezugriff besteht:
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 Erweiterungsdateien OBB danach nicht und speichern Sie die entpackten Daten nicht im selben Verzeichnis. Sie sollten die entpackten Dateien im Verzeichnis speichern, das durch getExternalFilesDir() angegeben wird. Wenn möglich, sollten Sie jedoch ein Erweiterungsdateiformat verwenden, das es Ihnen ermöglicht, direkt aus der Datei zu lesen, 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 verpacken, können Sie Media-Wiedergabeaufrufe für die Dateien mit Offset- und Längensteuerungen (z. B. MediaPlayer.setDataSource() und SoundPool.load()) verwenden, ohne die ZIP-Datei entpacken zu müssen. Damit das funktioniert, dürfen Sie die Mediendateien beim Erstellen der ZIP-Pakete nicht zusätzlich komprimieren. Wenn Sie beispielsweise das zip-Tool verwenden, sollten Sie mit der Option -n die Dateiendungen angeben, die nicht komprimiert werden sollen:
zip -n .mp4;.ogg main_expansion media_files
Downloadvorgang
In den meisten Fällen lädt Google Play Ihre Erweiterungsdateien gleichzeitig mit der APK-Datei auf das Gerät herunter und speichert sie. In einigen Fällen kann Google Play die Erweiterungsdateien jedoch nicht herunterladen oder der Nutzer hat zuvor heruntergeladene Erweiterungsdateien gelöscht. Um diese Situationen zu bewältigen, muss Ihre App die Dateien selbst herunterladen können, wenn die Hauptaktivität gestartet wird. Dazu muss sie eine von Google Play bereitgestellte URL verwenden.
Der Downloadprozess sieht auf einer hohen Ebene so aus:
- Der Nutzer wählt aus, dass Ihre App über Google Play installiert werden soll.
- 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 Ihre App startet, muss Ihre App prüfen, ob die Erweiterungsdateien bereits auf dem Gerät gespeichert sind.
- Wenn ja, ist Ihre App bereit.
- Wenn nicht, muss Ihre App die Erweiterungsdateien über HTTP von Google Play herunterladen. Ihre App muss eine Anfrage an den Google Play-Client über den App-Lizenzierungsdienst von Google Play senden. Dieser antwortet mit dem Namen, der Dateigröße und der URL für jede Erweiterungsdatei. Anhand dieser Informationen laden Sie die Dateien herunter und speichern sie am richtigen Speicherort.
Achtung:Es ist wichtig, dass Sie den erforderlichen Code zum Herunterladen der Erweiterungsdateien von Google Play einfügen, falls die Dateien beim Start Ihrer App noch nicht auf dem Gerät vorhanden sind. Wie im folgenden Abschnitt zum Herunterladen der Erweiterungsdateien beschrieben, haben wir eine Bibliothek für Sie bereitgestellt, die diesen Prozess erheblich vereinfacht und den Download über einen Dienst mit einem minimalen Codeaufwand für Sie durchführt.
Checkliste für die Entwicklung
Hier finden Sie eine Zusammenfassung der Aufgaben, die Sie ausführen müssen, um Erweiterungsdateien mit Ihrer App zu verwenden:
- Prüfen Sie zuerst, ob die komprimierte Downloadgröße Ihrer App mehr als 100 MB betragen muss. Speicherplatz ist kostbar. Halten Sie die Gesamtgröße Ihrer Downloads so gering wie möglich. Wenn Ihre App mehr als 100 MB Speicherplatz benötigt, um mehrere Versionen Ihrer Grafikinhalte für verschiedene Bildschirmdichten bereitzustellen, sollten Sie stattdessen mehrere APKs veröffentlichen, in denen jedes APK nur die Assets enthält, die für die Zielbildschirme erforderlich sind. Wenn Sie Ihre App in Google Play veröffentlichen, sollten Sie für optimale Ergebnisse ein Android App Bundle hochladen. Dieses enthält den gesamten kompilierten Code und alle Ressourcen Ihrer App, die APK-Generierung und -Signierung werden jedoch an Google Play delegiert.
- Legen Sie fest, welche App-Ressourcen Sie von Ihrem APK trennen und in einer Datei verpacken möchten, die als Haupterweiterungsdatei verwendet werden soll.
Normalerweise sollten Sie die zweite Patch-Erweiterungsdatei nur verwenden, wenn Sie die Haupt-Erweiterungsdatei aktualisieren. Wenn Ihre Ressourcen jedoch das Limit von 2 GB für die Haupt-Erweiterungsdatei überschreiten, können Sie die Patchdatei für die restlichen Assets verwenden.
- Entwickeln Sie Ihre App so, dass sie die Ressourcen aus Ihren Erweiterungsdateien im gemeinsamen Speicherort des Geräts verwendet.
Sie dürfen die Erweiterungsdateien nicht löschen, verschieben oder umbenennen.
Wenn für Ihre App kein bestimmtes Format erforderlich ist, empfehlen wir, ZIP-Dateien für Ihre Erweiterungsdateien zu erstellen und sie dann mit der APK Expansion Zip Library zu lesen.
- Fügen Sie der Hauptaktivität Ihrer App Logik hinzu, die beim Start prüft, ob sich die Erweiterungsdateien 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 die Dateien dann herunter und speichern Sie sie.
Um die Menge des zu schreibenden Codes erheblich zu reduzieren und eine gute Nutzererfahrung während des Downloads zu gewährleisten, empfehlen wir, das Downloadverhalten mit der Downloader-Bibliothek zu implementieren.
Wenn Sie einen eigenen Download-Dienst erstellen, anstatt die Bibliothek zu verwenden, dürfen Sie den Namen der Erweiterungsdateien nicht ändern und müssen sie am richtigen Speicherort speichern.
Nachdem Sie die Entwicklung Ihrer App abgeschlossen haben, folgen Sie der Anleitung zum Testen Ihrer Erweiterungsdateien.
Regeln und Einschränkungen
Das Hinzufügen von APK-Erweiterungsdateien ist eine Funktion, die verfügbar ist, wenn Sie Ihre App über die Play Console hochladen. Wenn Sie Ihre App zum ersten Mal hochladen oder eine App aktualisieren, die Erweiterungsdateien verwendet, müssen Sie die folgenden Regeln und Einschränkungen beachten:
- Jede Erweiterungsdatei darf maximal 2 GB groß sein.
- Damit Sie Ihre Erweiterungsdateien von Google Play herunterladen können, muss der Nutzer Ihre App bei Google Play erworben haben. Google Play stellt die URLs für Ihre Erweiterungsdateien nicht zur Verfügung, wenn die App auf andere Weise installiert wurde.
- Wenn der Download über Ihre App erfolgt, ist die URL, die Google Play für jede Datei bereitstellt, für jeden Download eindeutig und läuft kurz nach der Bereitstellung für 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. Er behält die Version bei, die vom APK empfangen wurde, dem die Datei ursprünglich zugeordnet war.
- Wenn Sie Erweiterungsdateien in Kombination mit mehreren APKs verwenden, um verschiedene Erweiterungsdateien für verschiedene Geräte bereitzustellen, müssen Sie weiterhin separate APKs für jedes Gerät hochladen, um einen eindeutigen
versionCode-Wert anzugeben und verschiedene Filter für jedes APK zu deklarieren. - Sie können Ihre App nicht aktualisieren, indem Sie nur die Erweiterungsdateien ändern. Sie müssen ein neues APK hochladen, um Ihre App zu aktualisieren. Wenn sich Ihre Änderungen nur auf die Assets in Ihren Erweiterungsdateien beziehen, können Sie Ihr APK aktualisieren, indem Sie einfach die
versionCode(und möglicherweise auch dieversionName) ändern. - Speichern Sie keine anderen Daten im Verzeichnis
obb/. Wenn Sie einige Daten entpacken müssen, speichern Sie sie am vongetExternalFilesDir()angegebenen Speicherort. - Löschen oder benennen Sie die Erweiterungsdatei
.obbnicht um, es sei denn, Sie führen ein Update durch. Dadurch wird die Erweiterungsdatei wiederholt von Google Play (oder Ihrer App selbst) heruntergeladen. - 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 auf das Gerät herunter und speichert sie dort, während die 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, indem sie sie über eine URL anfordert, die Ihnen in einer Antwort vom App-Lizenzierungsdienst von Google Play zur Verfügung gestellt wird.
Die grundlegende Logik, die Sie zum Herunterladen Ihrer Erweiterungsdateien benötigen, ist folgende:
- Wenn Ihre App startet, suchen Sie im gemeinsamen Speicherort (im Verzeichnis
Android/obb/<package-name>/) nach den Erweiterungsdateien.- Wenn die Erweiterungsdateien vorhanden sind, ist alles in Ordnung und Ihre App kann fortgesetzt werden.
- Wenn die Erweiterungsdateien nicht vorhanden sind:
- Führen Sie eine Anfrage über die App-Lizenzierung von Google Play aus, um die Namen, Größen und URLs der Erweiterungsdateien Ihrer App abzurufen.
- Verwenden Sie die von Google Play bereitgestellten URLs, um die Erweiterungsdateien herunterzuladen und zu speichern. Sie müssen die Dateien am Speicherort für freigegebene Dateien (
Android/obb/<package-name>/) speichern und genau den Dateinamen verwenden, der in der Antwort von Google Play angegeben ist.Hinweis:Die URL, die Google Play für Ihre Erweiterungsdateien bereitstellt, ist für jeden Download eindeutig und läuft kurz nach der Bereitstellung für Ihre App ab.
Wenn Ihre App kostenlos ist, haben Sie den Dienst App-Lizenzierung wahrscheinlich nicht verwendet. Sie ist in erster Linie dafür gedacht, dass Sie Lizenzierungsrichtlinien für Ihre App durchsetzen und sicherstellen können, dass der Nutzer das Recht hat, Ihre App zu verwenden (er hat sie rechtmäßig bei Google Play bezahlt). Um die Funktion für Erweiterungsdateien zu ermöglichen, wurde der Lizenzierungsdienst so erweitert, dass er eine Antwort an Ihre App sendet, die die URL der Erweiterungsdateien Ihrer App enthält, die auf Google Play gehostet werden. Auch wenn Ihre App für Nutzer kostenlos ist, müssen Sie die License Verification Library (LVL) einbinden, um APK-Erweiterungsdateien verwenden zu können. Wenn Ihre App kostenlos ist, müssen Sie die Lizenzüberprüfung natürlich nicht erzwingen. Sie benötigen die Bibliothek lediglich, 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 URLs der Erweiterungsdateien nur zurück, wenn der Nutzer Ihre App über Google Play erworben hat.
Zusätzlich zur LVL benötigen Sie Code, der die Erweiterungsdateien über eine HTTP-Verbindung herunterlädt und am richtigen Speicherort auf dem freigegebenen Speicher des Geräts speichert. Bei der Implementierung dieses Verfahrens in Ihre App sollten Sie Folgendes berücksichtigen:
- Das Gerät hat möglicherweise nicht genügend Speicherplatz für die Erweiterungsdateien. Prüfen Sie das daher vor dem Herunterladen und warnen Sie den Nutzer, wenn nicht genügend Speicherplatz vorhanden ist.
- Dateidownloads sollten in einem Hintergrunddienst erfolgen, um die Nutzerinteraktion nicht zu blockieren und dem Nutzer zu ermöglichen, die App zu verlassen, während der Download abgeschlossen wird.
- Während der Anfrage und des Downloads können verschiedene Fehler auftreten, die Sie ordnungsgemäß behandeln müssen.
- Die Netzwerkverbindung kann sich während des Downloads ändern. Sie sollten solche Änderungen berücksichtigen und den Download bei einer Unterbrechung nach Möglichkeit fortsetzen.
- Während des Downloads im Hintergrund sollten Sie eine Benachrichtigung anzeigen, die den Downloadfortschritt angibt, den Nutzer benachrichtigt, wenn der Download abgeschlossen ist, und den Nutzer bei Auswahl zurück zu Ihrer App führt.
Um Ihnen diese Arbeit zu erleichtern, haben wir die Downloader-Bibliothek entwickelt. Sie fordert die URLs der Erweiterungsdateien über den Lizenzierungsdienst an, lädt die Erweiterungsdateien herunter, führt alle oben aufgeführten Aufgaben aus und ermöglicht sogar, den Download zu pausieren und fortzusetzen. Wenn Sie die Downloader-Bibliothek und einige Code-Hooks in Ihre App einfügen, ist fast die gesamte Arbeit zum Herunterladen der Erweiterungsdateien bereits für Sie erledigt. Damit Sie Ihren Nutzern eine optimale Nutzererfahrung bieten können, ohne dass Sie selbst viel Aufwand haben, empfehlen wir Ihnen, die Downloader Library zum Herunterladen Ihrer Erweiterungsdateien zu verwenden. In den folgenden Abschnitten wird beschrieben, wie Sie die Bibliothek in Ihre App einbinden.
Wenn Sie lieber eine eigene Lösung zum Herunterladen der Erweiterungsdateien über die Google Play-URLs entwickeln möchten, müssen Sie der App-Lizenzierung folgen, um eine Lizenzanfrage zu stellen. Rufen Sie dann die Namen, Größen und URLs der Erweiterungsdateien aus den Antwort-Extras ab. Sie sollten die Klasse APKExpansionPolicy (in der Lizenzüberprüfungsbibliothek enthalten) als Lizenzrichtlinie verwenden, da sie die Namen, Größen und URLs der Erweiterungsdateien aus dem Lizenzierungsdienst erfasst.
Downloader-Bibliothek
Wenn Sie APK-Erweiterungsdateien mit Ihrer App verwenden und Nutzern mit minimalem Aufwand ein optimales Nutzererlebnis bieten möchten, empfehlen wir Ihnen, die Downloader Library zu verwenden, die im Paket „Google Play APK Expansion Library“ enthalten ist. Mit dieser Bibliothek werden Ihre Erweiterungsdateien in einem Hintergrunddienst heruntergeladen. Außerdem wird eine Benachrichtigung mit dem Downloadstatus angezeigt, der Verlust der Netzwerkverbindung wird behandelt und der Download wird bei Bedarf fortgesetzt.
So implementieren Sie das Herunterladen von Erweiterungsdateien mit der Downloader Library:
- Erstellen Sie eine Erweiterung einer speziellen
Service-Unterklasse und einerBroadcastReceiver-Unterklasse, für die jeweils nur wenige Zeilen Code von Ihnen erforderlich sind. - Fügen Sie Ihrer Hauptaktivität etwas Logik hinzu, um zu prüfen, ob die Erweiterungsdateien bereits heruntergeladen wurden. Wenn nicht, rufen Sie den Downloadprozess auf und zeigen Sie eine Fortschrittsoberfläche an.
- Implementieren Sie in Ihrer Hauptaktivität eine Callback-Oberfläche mit einigen Methoden, die Updates zum Downloadfortschritt empfangen.
In den folgenden Abschnitten wird beschrieben, wie Sie Ihre App mit der Downloader-Bibliothek einrichten.
Verwendung der Downloader-Bibliothek vorbereiten
Wenn Sie die Downloader-Bibliothek verwenden möchten, müssen Sie zwei Pakete aus dem SDK Manager herunterladen und die entsprechenden Bibliotheken zu Ihrer App hinzufügen.
Öffnen Sie zuerst den Android SDK Manager (Tools > SDK Manager) und wählen Sie unter Appearance & Behavior > System Settings > Android SDK den Tab SDK Tools aus, um Folgendes auszuwählen und herunterzuladen:
- Google Play Licensing Library-Paket
- Google Play APK Expansion Library-Paket
Erstellen Sie ein neues Bibliotheksmodul für die Bibliothek für Lizenzbestätigungen und die Downloader-Bibliothek. Für jede Bibliothek:
- Wählen Sie Datei > Neu > Neues Modul aus.
- Wählen Sie im Fenster Neues Modul erstellen die Option Android-Bibliothek und dann Weiter aus.
- Geben Sie einen App-/Bibliotheksnamen an, z. B. „Google Play License Library“ (Google Play-Lizenzbibliothek) und „Google Play Downloader Library“ (Google Play-Downloader-Bibliothek), wählen Sie das Mindest-SDK-Level aus und klicken Sie dann auf Fertigstellen.
- Wählen Sie File > Project Structure (Datei > Projektstruktur) aus.
- Wählen Sie den Tab Properties (Eigenschaften) aus und geben Sie im Library Repository (Bibliotheks-Repository) die Bibliothek aus dem Verzeichnis
<sdk>/extras/google/ein (play_licensing/für die License Verification Library oderplay_apk_expansion/downloader_library/für die Downloader Library). - Wählen Sie OK aus, um das neue Modul zu erstellen.
Hinweis:Die Downloader-Bibliothek ist von der Lizenzüberprüfungsbibliothek abhängig. Fügen Sie die Lizenzüberprüfungsbibliothek den Projekteigenschaften der Downloader-Bibliothek hinzu.
Alternativ können Sie Ihr Projekt über die Befehlszeile aktualisieren, um die Bibliotheken einzubinden:
- Wechseln Sie in das Verzeichnis
<sdk>/tools/. - Führen Sie
android update projectmit der Option--libraryaus, um sowohl die LVL als auch die Downloader-Bibliothek zu Ihrem Projekt 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 sowohl die License Verification Library als auch die Downloader Library in Ihre App einfügen, können Sie die Möglichkeit, Erweiterungsdateien von Google Play herunterzuladen, schnell integrieren. Das Format, das Sie für die Erweiterungsdateien auswählen, und die Art und Weise, wie Sie sie aus dem freigegebenen Speicher lesen, ist eine separate Implementierung, die Sie je nach den Anforderungen Ihrer App berücksichtigen 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 Drittanbieterbibliothek verwendet, die im APK-Erweiterungspaket verfügbar ist und als APK Expansion Zip Library bezeichnet wird. Wenn Sie ZIP-Dateien für Ihre Erweiterungsdateien verwenden möchten, empfehlen wir, Ihrer App auch die APK Expansion Zip Library hinzuzufügen. Weitere Informationen finden Sie unten im Abschnitt APK Expansion Zip Library verwenden.
Nutzerberechtigungen deklarieren
Damit die Erweiterungsdateien heruntergeladen werden können, benötigt die Downloader-Bibliothek mehrere Berechtigungen, die Sie in der Manifestdatei Ihrer App deklarieren müssen. Das 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 ist für die Downloader-Bibliothek API-Level 4 erforderlich, für die APK Expansion Zip-Bibliothek jedoch 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. Das DownloaderService lädt nicht nur die Erweiterungsdateien für Sie herunter, sondern:
- Registriert einen
BroadcastReceiver, der auf Änderungen der Netzwerkverbindung des Geräts (denCONNECTIVITY_ACTION-Broadcast) wartet, um den Download bei Bedarf (z. B. bei Verbindungsverlust) zu pausieren und den Download fortzusetzen, wenn dies möglich ist (Verbindung wird hergestellt). - Plant einen
RTC_WAKEUP-Alarm, um den Download in Fällen, in denen der Dienst beendet wird, noch einmal zu versuchen. - Erstellt ein benutzerdefiniertes
Notification, in dem der Downloadfortschritt und alle Fehler oder Statusänderungen angezeigt werden. - Ermöglicht Ihrer App, den Download manuell zu pausieren und fortzusetzen.
- Prüft, ob der freigegebene Speicher gemountet und verfügbar ist, ob die Dateien noch nicht vorhanden sind und ob genügend Speicherplatz vorhanden ist, bevor die Erweiterungsdateien heruntergeladen werden. Der Nutzer wird benachrichtigt, wenn eine dieser Bedingungen nicht erfüllt ist.
Sie müssen lediglich eine Klasse in Ihrer App erstellen, die die Klasse DownloaderService erweitert, und drei Methoden überschreiben, um spezifische App-Details anzugeben:
getPublicKey()- Diese Funktion muss einen String zurückgeben, der den Base64-codierten öffentlichen RSA-Schlüssel für Ihr Publisher-Konto enthält. Sie finden ihn auf der Profilseite in der Play Console (siehe Lizenzierung einrichten).
getSALT()- Diese Funktion muss ein Array mit zufälligen Byte zurückgeben, das von der Lizenzierung
Policyverwendet wird, um eineObfuscatorzu erstellen. Das Salt sorgt dafür, dass Ihre verschleierteSharedPreferences-Datei, in der Ihre Lizenzierungsdaten gespeichert sind, eindeutig und nicht auffindbar ist. getAlarmReceiverClassName()- Hier muss der Klassenname der
BroadcastReceiverin Ihrer App zurückgegeben werden, die den Alarm empfangen soll, der angibt, dass der Download neu gestartet werden soll (was passieren kann, wenn der Downloader-Dienst unerwartet beendet wird).
Hier ist 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:Sie müssen den Wert BASE64_PUBLIC_KEY auf den öffentlichen Schlüssel Ihres Publisher-Kontos aktualisieren. Sie finden den Schlüssel in der Developer Console unter Ihren Profilinformationen. Das ist auch beim Testen Ihrer Downloads erforderlich.
Denken Sie daran, den Dienst in Ihrer Manifestdatei zu deklarieren:
<app ...> <service android:name=".SampleDownloaderService" /> ... </app>
Weckempfänger implementieren
Um den Fortschritt der Dateidownloads zu überwachen und den Download bei Bedarf neu zu starten, plant DownloaderService einen RTC_WAKEUP-Alarm, der ein Intent an ein BroadcastReceiver in Ihrer App sendet. Sie müssen die BroadcastReceiver definieren, um eine API aus der Downloader-Bibliothek aufzurufen, die den Status des Downloads prüft und ihn bei Bedarf neu startet.
Sie müssen lediglich die Methode onReceive() überschreiben, um DownloaderClientMarshaller.startDownloadServiceIfRequired() aufzurufen.
Beispiel:
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 getAlarmReceiverClassName()-Methode Ihres Dienstes zurückgeben müssen (siehe vorheriger Abschnitt).
Denken Sie daran, den Receiver in Ihrer Manifestdatei zu deklarieren:
<app ...> <receiver android:name=".SampleAlarmReceiver" /> ... </app>
Download starten
Die Hauptaktivität in Ihrer App (die über das Launcher-Symbol gestartet wird) ist dafür verantwortlich, zu prüfen, ob sich die Erweiterungsdateien bereits auf dem Gerät befinden, und den Download zu starten, wenn dies nicht der Fall ist.
Wenn Sie den Download mit der Downloader-Bibliothek starten möchten, sind folgende Schritte erforderlich:
- Prüfen Sie, ob die Dateien heruntergeladen wurden.
Die Downloader-Bibliothek enthält einige APIs in der Klasse
Helper, die bei diesem Prozess helfen:getExpansionAPKFileName(Context, c, boolean mainFile, int versionCode)doesFileExist(Context c, String fileName, long fileSize)
In der Beispiel-App, die im APK-Erweiterungspaket enthalten ist, wird beispielsweise die folgende Methode in der
onCreate()-Methode der Aktivität aufgerufen, 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 Dateigröße einer bekannten Erweiterungsdatei sowie einen booleschen Wert, der angibt, ob es sich um die Haupterweiterungsdatei handelt. Weitere Informationen finden Sie in derSampleDownloaderActivity-Klasse der Beispiel-App.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 verwendet die folgenden Parameter:
context: DieContextIhrer App.notificationClient: EinPendingIntentzum Starten der Hauptaktivität. Dies wird in derNotificationverwendet, die vonDownloaderServiceerstellt wird, um den Downloadfortschritt anzuzeigen. Wenn der Nutzer die Benachrichtigung auswählt, ruft das System diePendingIntentauf, die Sie hier angeben, und sollte die Aktivität öffnen, in der der Downloadfortschritt angezeigt wird (in der Regel dieselbe Aktivität, die den Download gestartet hat).serviceClass: DasClass-Objekt für Ihre Implementierung vonDownloaderService, das zum Starten des Dienstes und zum Starten des Downloads (falls erforderlich) benötigt wird.
Die Methode gibt eine Ganzzahl zurück, die angibt, ob der Download erforderlich ist. Folgende Werte sind möglich:
NO_DOWNLOAD_REQUIRED: Wird zurückgegeben, wenn die Dateien bereits vorhanden sind oder ein Download bereits läuft.LVL_CHECK_REQUIRED: Wird zurückgegeben, wenn eine Lizenzüberprüfung erforderlich ist, um die URLs der Erweiterungsdateien abzurufen.DOWNLOAD_REQUIRED: Wird zurückgegeben, wenn die URLs der Erweiterungsdateien bereits bekannt sind, aber noch nicht heruntergeladen wurden.
Das Verhalten für
LVL_CHECK_REQUIREDundDOWNLOAD_REQUIREDist im Wesentlichen dasselbe und Sie müssen sich normalerweise keine Gedanken darüber machen. In Ihrer Hauptaktivität, diestartDownloadServiceIfRequired()aufruft, können Sie einfach prüfen, ob die AntwortNO_DOWNLOAD_REQUIREDist. Wenn die Antwort etwas anderes alsNO_DOWNLOAD_REQUIREDist, beginnt die Downloader-Bibliothek mit dem Download. Sie sollten die UI Ihrer Aktivität aktualisieren, um den Downloadfortschritt anzuzeigen (siehe nächsten Schritt). Wenn die Antwort isNO_DOWNLOAD_REQUIREDlautet, sind die Dateien verfügbar und Ihre App kann gestartet werden.Beispiel:
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
startDownloadServiceIfRequired()-Methode etwas anderes alsNO_DOWNLOAD_REQUIREDzurückgibt, erstellen Sie eine Instanz vonIStub, indem SieDownloaderClientMarshaller.CreateStub(IDownloaderClient client, Class<?> downloaderService)aufrufen. DieIStubstellt eine Bindung zwischen Ihrer Aktivität und dem Downloader-Dienst her, sodass Ihre Aktivität Rückrufe zum Downloadfortschritt erhält.Wenn Sie
IStubdurch Aufrufen vonCreateStub()instanziieren möchten, müssen Sie eine Implementierung derIDownloaderClient-Schnittstelle und IhreDownloaderService-Implementierung übergeben. Im nächsten Abschnitt zum Empfangen des Downloadfortschritts wird die SchnittstelleIDownloaderClientbeschrieben, 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, umIStubin der MethodeonCreate()Ihrer Aktivität zu instanziieren, nachdemstartDownloadServiceIfRequired()den Download gestartet hat.Im vorherigen Codebeispiel für
onCreate()können Sie beispielsweise so auf das ErgebnisstartDownloadServiceIfRequired()reagieren: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, wird in Ihrer AktivitätonResume()aufgerufen. Dort sollten Sie dannconnect()fürIStubaufrufen und dieContextIhrer 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(); }
Wenn Sie
connect()fürIStubaufrufen, wird Ihre Aktivität anDownloaderServicegebunden, sodass Ihre Aktivität über dieIDownloaderClient-Schnittstelle Callbacks zu Änderungen am Downloadstatus erhält.
Downloadfortschritt wird empfangen
Wenn Sie Updates zum Downloadfortschritt erhalten und mit dem DownloaderService interagieren möchten, müssen Sie die IDownloaderClient-Schnittstelle der Downloader Library implementieren.
Normalerweise sollte die Aktivität, mit der Sie den Download starten, diese Schnittstelle implementieren, um den Downloadfortschritt anzuzeigen und Anfragen an den Dienst zu senden.
Die erforderlichen Schnittstellenmethoden für IDownloaderClient sind:
onServiceConnected(Messenger m)- Nachdem Sie
IStubin Ihrer Aktivität instanziiert haben, wird diese Methode aufgerufen und einMessenger-Objekt übergeben, das mit Ihrer Instanz vonDownloaderServiceverbunden ist. Wenn Sie Anfragen an den Dienst senden möchten, z. B. um Downloads zu pausieren und fortzusetzen, müssen SieDownloaderServiceMarshaller.CreateProxy()aufrufen, um die mit dem Dienst verbundeneIDownloaderService-Schnittstelle zu erhalten.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()); }
Nachdem das
IDownloaderService-Objekt initialisiert wurde, können Sie Befehle an den Downloader-Dienst senden, z. B. zum Pausieren und Fortsetzen des Downloads (requestPauseDownload()undrequestContinueDownload()). onDownloadStateChanged(int newState)- Der Download-Dienst ruft diese Methode auf, wenn sich der Download-Status ändert, z. B. wenn der Download beginnt oder abgeschlossen wird.
Der
newState-Wert ist einer von mehreren möglichen Werten, die in einer derSTATE_*-Konstanten der KlasseIDownloaderClientangegeben sind.Damit Sie Ihren Nutzern eine hilfreiche Meldung anzeigen können, können Sie mit dem Aufruf von
Helpers.getDownloaderStringResourceIDFromState()einen entsprechenden String für jeden Status anfordern. Dadurch wird die Ressourcen-ID für einen der Strings zurückgegeben, die in der Downloader-Bibliothek enthalten sind. Der String „Download paused because you are roaming“ (Download pausiert, da Sie sich im Roaming befinden) entspricht beispielsweiseSTATE_PAUSED_ROAMING. onDownloadProgress(DownloadProgressInfo progress)- Der Download-Dienst ruft diese Methode auf, um ein
DownloadProgressInfo-Objekt zu übermitteln, das verschiedene Informationen zum Downloadfortschritt enthält, darunter die geschätzte verbleibende Zeit, die aktuelle Geschwindigkeit, den Gesamtfortschritt und die Gesamtdauer. So können Sie die Benutzeroberfläche für den Downloadfortschritt aktualisieren.
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 im Apk Expansion-Paket enthalten ist.
Einige öffentliche Methoden für die IDownloaderService-Schnittstelle, die für Sie nützlich sein könnten:
requestPauseDownload()- Pausiert den Download.
requestContinueDownload()- Setzt einen pausierten Download fort.
setDownloadFlags(int flags)- Legt die Nutzereinstellungen für Netzwerktypen fest, in denen Dateien heruntergeladen werden dürfen. Die aktuelle Implementierung unterstützt ein Flag,
FLAGS_DOWNLOAD_OVER_CELLULAR, aber Sie können weitere hinzufügen. Standardmäßig ist dieses Flag nicht aktiviert. Der Nutzer muss also mit einem WLAN verbunden sein, um Erweiterungsdateien herunterzuladen. Möglicherweise möchten Sie eine Nutzereinstellung anbieten, um Downloads über das Mobilfunknetz zu ermöglichen. In diesem Fall können Sie Folgendes anrufen:Kotlin
remoteService = DownloaderServiceMarshaller.CreateProxy(m).apply { ... setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR) }
Java
remoteService .setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);
APKExpansionPolicy verwenden
Wenn Sie einen eigenen Downloader-Dienst erstellen möchten, anstatt die Downloader-Bibliothek von Google Play zu verwenden, sollten Sie trotzdem die APKExpansionPolicy verwenden, die in der Lizenzüberprüfungsbibliothek enthalten ist. Die Klasse APKExpansionPolicy ist fast identisch mit ServerManagedPolicy (verfügbar in der Google Play License Verification Library), enthält aber zusätzliche Verarbeitung für die Antwort-Extras der APK-Erweiterungsdatei.
Hinweis:Wenn Sie die Downloader-Bibliothek wie im vorherigen Abschnitt beschrieben verwenden, übernimmt die Bibliothek die gesamte Interaktion mit dem APKExpansionPolicy. Sie müssen diese Klasse also nicht direkt verwenden.
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 die Downloader-Mediathek nicht verwenden, finden Sie in der Dokumentation unter Lizenzierung für Ihre App hinzufügen. Dort wird beschrieben, wie Sie eine Lizenzrichtlinie wie diese implementieren.
Erweiterungsdatei lesen
Nachdem Ihre APK-Erweiterungsdateien auf dem Gerät gespeichert wurden, hängt es vom Dateityp ab, wie Sie die Dateien lesen. Wie in der Übersicht beschrieben, können Ihre Erweiterungsdateien beliebige Dateitypen sein. Sie werden jedoch mit einem bestimmten Dateinamenformat umbenannt und in <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 kann sein, dass der Nutzer den Speicher über USB auf einem Computer gemountet oder die SD-Karte tatsächlich entfernt hat.
Hinweis:Wenn Ihre App gestartet wird, sollten Sie immer prüfen, ob der externe Speicherplatz verfügbar und lesbar ist. Rufen Sie dazu getExternalStorageState() auf. Es wird einer von mehreren möglichen Strings zurückgegeben, die den Status des externen Speichers darstellen. Damit die App den Wert lesen kann, muss der Rückgabewert MEDIA_MOUNTED sein.
Dateinamen abrufen
Wie in der Übersicht beschrieben, werden Ihre APK-Erweiterungsdateien mit einem bestimmten Dateinamenformat gespeichert:
[main|patch].<expansion-version>.<package-name>.obb
Verwenden Sie die Methoden getExternalStorageDirectory() und getPackageName(), um den Pfad zu Ihren Dateien zu erstellen und so den Speicherort und die Namen Ihrer Erweiterungsdateien zu ermitteln.
Hier ist eine Methode, die Sie in Ihrer App verwenden können, um ein Array mit dem vollständigen Pfad zu beiden Erweiterungsdateien abzurufen:
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 ihr die Context Ihrer App und die gewünschte Version der Erweiterungsdatei übergeben.
Es gibt viele Möglichkeiten, die Versionsnummer der Erweiterungsdatei zu ermitteln. Eine einfache Möglichkeit besteht darin, die Version in einer SharedPreferences-Datei zu speichern, wenn der Download beginnt. Dazu fragen Sie den Namen der Erweiterungsdatei mit der Methode getExpansionFileName(int index) der Klasse APKExpansionPolicy ab. Sie können den Versionscode dann abrufen, indem Sie die SharedPreferences-Datei lesen, wenn Sie auf die Erweiterungsdatei zugreifen möchten.
Weitere Informationen zum Lesen aus dem gemeinsamen Speicher finden Sie in der Dokumentation Datenspeicher.
APK Expansion Zip Library verwenden
Das Google Market Apk Expansion-Paket enthält eine Bibliothek namens „APK Expansion Zip Library“ (im Verzeichnis <sdk>/extras/google/google_market_apk_expansion/zip_file/). Diese optionale Bibliothek hilft Ihnen, Ihre Erweiterungsdateien zu lesen, wenn sie als ZIP-Dateien gespeichert sind. Mit dieser Bibliothek können Sie Ressourcen aus Ihren ZIP-Erweiterungsdateien ganz einfach als virtuelles Dateisystem lesen.
Die APK Expansion Zip Library enthält die folgenden Klassen und APIs:
APKExpansionSupport- Bietet einige Methoden für den Zugriff auf Erweiterungsdateinamen und ZIP-Dateien:
getAPKExpansionFiles()- Dieselbe Methode wie oben, die den vollständigen Dateipfad zu beiden Erweiterungsdateien zurückgibt.
getAPKExpansionZipFile(Context ctx, int mainVersion, int patchVersion)- Gibt eine
ZipResourceFilezurück, die die Summe aus Hauptdatei und Patchdatei darstellt. Wenn Sie sowohlmainVersionals auchpatchVersionangeben, wird einZipResourceFilezurückgegeben, das Lesezugriff auf alle Daten bietet, wobei die Daten der Patchdatei über die Hauptdatei gelegt werden.
ZipResourceFile- Stellt eine ZIP-Datei im gemeinsam genutzten Speicher dar und führt alle erforderlichen Schritte aus, um ein virtuelles Dateisystem auf Grundlage Ihrer ZIP-Dateien bereitzustellen. Sie können eine Instanz mit
APKExpansionSupport.getAPKExpansionZipFile()oder mitZipResourceFileabrufen, indem Sie den Pfad zu Ihrer Erweiterungsdatei übergeben. Diese Klasse enthält eine Vielzahl nützlicher Methoden, auf die Sie in der Regel nicht zugreifen müssen. Einige wichtige Methoden sind:getInputStream(String assetPath)- Stellt eine
InputStreamzum Lesen einer Datei in der ZIP-Datei bereit.assetPathmuss der Pfad zur gewünschten Datei sein, relativ zum Stammverzeichnis des ZIP-Dateiinhalts. getAssetFileDescriptor(String assetPath)- Gibt einen
AssetFileDescriptorfür eine Datei in der ZIP-Datei an.assetPathmuss der Pfad zur gewünschten Datei sein, relativ zum Stammverzeichnis des ZIP-Dateiinhalts. Das ist für bestimmte Android-APIs nützlich, die einAssetFileDescriptorerfordern, z. B. einigeMediaPlayer-APIs.
APEZProvider- Die meisten Apps müssen diese Klasse nicht verwenden. Diese Klasse definiert einen
ContentProvider, der die Daten aus den ZIP-Dateien über einen ContentanbieterUriserialisiert, um Dateizugriff für bestimmte Android-APIs bereitzustellen, dieUri-Zugriff auf Mediendateien erwarten. Das ist beispielsweise nützlich, wenn Sie ein Video mitVideoView.setVideoURI()abspielen möchten.
ZIP-Komprimierung von Mediendateien überspringen
Wenn Sie Ihre Erweiterungsdateien zum Speichern von Media-Dateien verwenden, können Sie mit einer ZIP-Datei weiterhin Android-Media-Wiedergabeaufrufe mit Offset- und Längensteuerungen (z. B. MediaPlayer.setDataSource() und SoundPool.load()) verwenden. Damit dies funktioniert, dürfen Sie die Media-Dateien beim Erstellen der ZIP-Pakete nicht zusätzlich komprimieren. Wenn Sie beispielsweise das zip-Tool verwenden, sollten Sie mit der Option -n die Dateiendungen angeben, die nicht komprimiert werden sollen:
zip -n .mp4;.ogg main_expansion media_files
Daten aus einer ZIP-Datei lesen
Wenn Sie die APK Expansion Zip Library verwenden, ist zum Lesen einer Datei aus Ihrer ZIP-Datei in der Regel Folgendes erforderlich:
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 oben stehende Code ermöglicht den Zugriff auf jede Datei, die entweder in der Haupt-Erweiterungsdatei oder in der Patch-Erweiterungsdatei vorhanden ist, indem er aus einer zusammengeführten Karte aller Dateien aus beiden Dateien liest. Für die getAPKExpansionFile()-Methode müssen Sie nur die android.content.Context Ihrer App und die Versionsnummer für die Haupt- und die Patch-Erweiterungsdatei angeben.
Wenn Sie lieber aus einer bestimmten Erweiterungsdatei lesen möchten, können Sie den ZipResourceFile-Konstruktor 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 SampleDownloaderActivity-Klasse der Beispiel-App. 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 Byte-Größe Ihrer Erweiterungsdateien im xAPKS-Array deklarieren.
Erweiterungsdateien testen
Bevor Sie Ihre App veröffentlichen, sollten Sie zwei Dinge testen: das Lesen der Erweiterungsdateien und das Herunterladen der Dateien.
Lesevorgänge für Dateien testen
Bevor Sie Ihre App bei Google Play hochladen, sollten Sie testen, ob sie Dateien aus dem freigegebenen Speicher lesen kann. Sie müssen die Dateien nur an der entsprechenden Stelle im freigegebenen Speicher des Geräts hinzufügen und Ihre 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.androidlautet, müssen Sie das VerzeichnisAndroid/obb/com.example.android/im freigegebenen Speicherplatz erstellen. Schließen Sie Ihr Testgerät an Ihren Computer an, um den freigegebenen Speicher zu mounten und dieses Verzeichnis manuell zu erstellen. - Fügen Sie die Erweiterungsdateien manuell in dieses Verzeichnis ein. Achten Sie darauf, dass Sie Ihre Dateien entsprechend dem Dateinamenformat umbenennen, das von Google Play verwendet wird.
Unabhängig vom Dateityp sollte die Haupterweiterungsdatei für die App
com.example.androidbeispielsweisemain.0300110.com.example.android.obbsein. Der Versionscode kann ein beliebiger Wert sein. Denken Sie daran:- Die Haupterweiterungsdatei beginnt immer mit
mainund die Patchdatei mitpatch. - Der Paketname stimmt immer mit dem der APK überein, an die die Datei in Google Play angehängt ist.
- Die Haupterweiterungsdatei beginnt immer mit
- Nachdem sich die Erweiterungsdatei(en) auf dem Gerät befinden, können Sie Ihre App installieren und ausführen, um die Erweiterungsdatei(en) zu testen.
Hier sind einige Hinweise zum Umgang mit den Erweiterungsdateien:
- Löschen oder benennen Sie die
.obb-Erweiterungsdateien nicht um, auch wenn Sie die Daten an einem anderen Ort entpacken. Dadurch wird die Erweiterungsdatei wiederholt von Google Play (oder Ihrer App selbst) heruntergeladen. - Speichern Sie keine anderen Daten im Verzeichnis
obb/. Wenn Sie einige Daten entpacken müssen, speichern Sie sie am vongetExternalFilesDir()angegebenen Speicherort.
Dateidownloads testen
Da Ihre App die Erweiterungsdateien manchmal manuell herunterladen muss, wenn sie zum ersten Mal geöffnet wird, ist es wichtig, dass Sie diesen Vorgang testen, um sicherzugehen, dass Ihre App die URLs erfolgreich abfragen, die Dateien herunterladen und auf dem Gerät speichern kann.
Wenn Sie die Implementierung des manuellen Downloadverfahrens in Ihrer App testen möchten, können Sie sie im internen Test-Track veröffentlichen. Sie ist dann nur für autorisierte Tester verfügbar. Wenn alles wie erwartet funktioniert, sollten die Erweiterungsdateien heruntergeladen werden, sobald die Hauptaktivität gestartet wird.
Hinweis:Bisher konnten Sie eine App testen, indem Sie eine unveröffentlichte „Entwurfsversion“ hochgeladen haben. Diese Funktion wird nicht mehr unterstützt. Stattdessen müssen Sie sie in einem internen, geschlossenen oder offenen Test-Track veröffentlichen. Weitere Informationen finden Sie unter Draft Apps are No Longer Supported.
App aktualisieren
Einer der großen Vorteile der Verwendung von Erweiterungsdateien bei Google Play ist die Möglichkeit, Ihre App zu aktualisieren, ohne alle ursprünglichen Assets noch einmal herunterladen zu müssen. Da Sie bei Google Play zwei Erweiterungsdateien pro APK bereitstellen können, können Sie die zweite Datei als „Patch“ verwenden, um Updates und neue Assets bereitzustellen. So muss die Haupt-Erweiterungsdatei nicht noch einmal heruntergeladen werden. Das kann für Nutzer teuer werden, da sie möglicherweise sehr groß ist.
Die Patch-Erweiterungsdatei ist technisch gesehen dieselbe wie die Haupt-Erweiterungsdatei. Weder das Android-System noch Google Play führen ein tatsächliches Patchen zwischen der Haupt- und der Patch-Erweiterungsdatei durch. Der App-Code muss alle erforderlichen Patches selbst ausführen.
Wenn Sie ZIP-Dateien als Erweiterungsdateien verwenden, können Sie mit der APK Expansion Zip Library, die im Apk Expansion-Paket 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, sollten Sie einfach die versionCode im Manifest aktualisieren.
Solange Sie die mit dem APK verknüpfte Haupt-Erweiterungsdatei in der Play Console nicht ändern, wird die Haupt-Erweiterungsdatei nicht von Nutzern heruntergeladen, die Ihre App bereits installiert haben. Vorhandene Nutzer erhalten nur das aktualisierte APK und die neue Patch-Erweiterungsdatei (die vorherige Haupt-Erweiterungsdatei wird beibehalten).
Hier sind einige Punkte, die Sie bei der Aktualisierung von Erweiterungsdateien beachten sollten:
- Es können jeweils nur zwei Erweiterungsdateien für Ihre App vorhanden sein. Eine Haupterweiterungsdatei und eine Patch-Erweiterungsdatei. Bei der Aktualisierung einer Datei löscht Google Play die vorherige Version. Das muss auch Ihre App tun, wenn sie manuelle Updates durchführt.
- Wenn Sie eine Patch-Erweiterungsdatei hinzufügen, wird Ihre App oder Haupterweiterungsdatei vom Android-System nicht gepatcht. Sie müssen Ihre App so gestalten, dass sie die Patch-Daten unterstützt. Das APK-Erweiterungspaket enthält jedoch eine Bibliothek für die Verwendung von ZIP-Dateien als Erweiterungsdateien. Damit werden die Daten aus der Patchdatei in die Haupterweiterungsdatei zusammengeführt, sodass Sie alle Erweiterungsdateidaten problemlos lesen können.