Bei Google Play darf die komprimierte APK, die Nutzer herunterladen, maximal 100 MB groß sein. Für die meisten Apps ist das mehr als genug Speicherplatz 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 bisher 100 MB überschritten hat, mussten Sie die zusätzlichen Ressourcen selbst hosten und herunterladen, wenn der Nutzer die App öffnete. Das Hosting 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 Ihrer App und stellt sie kostenlos auf dem Gerät bereit. Die Erweiterungsdateien werden im freigegebenen Speicherort des Geräts gespeichert (SD-Karte oder USB-Partition, auch als „externer“ Speicher bezeichnet), auf den Ihre App zugreifen kann. Auf den meisten Geräten lädt Google Play die Erweiterungsdatei(en) gleichzeitig mit dem APK herunter. Ihre App enthält also alles, was sie benötigt, wenn der Nutzer sie zum ersten Mal öffnet. In einigen Fällen müssen die Dateien jedoch beim Starten der App von Ihrer App aus Google Play heruntergeladen werden.
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 200 MB. Da bei der Verwendung von App-Bundles die APK-Generierung und -Signatur an Google Play übergeben wird, laden Nutzer optimierte APKs mit nur dem Code und den Ressourcen herunter, die sie zum Ausführen Ihrer App benötigen. Sie müssen keine mehrere APKs oder Erweiterungsdateien erstellen, signieren und verwalten. Nutzer erhalten kleinere, optimiertere Downloads.
Übersicht
Jedes Mal, wenn Sie ein APK über die Google Play Console hochladen, haben Sie die Möglichkeit, dem APK eine oder zwei Erweiterungsdateien hinzuzufügen. Jede Datei kann bis zu 2 GB groß sein und jedes beliebige Format haben. Wir empfehlen jedoch, eine komprimierte Datei zu verwenden, um die Bandbreite beim Download zu schonen. Jede Erweiterungsdatei hat eine unterschiedliche Rolle:
- Die Erweiterungsdatei main ist die Haupterweiterungsdatei für zusätzliche Ressourcen, die für Ihre App erforderlich sind.
- Die Erweiterungsdatei patch ist optional und dient für kleine Updates der Haupterweiterungsdatei.
Sie können die beiden Erweiterungsdateien verwenden, wie Sie möchten. Wir empfehlen jedoch, dass die Haupterweiterungsdatei die primären Assets enthält und nur selten oder gar nicht aktualisiert wird. Die Patch-Erweiterungsdatei sollte kleiner sein und als „Patch-Träger“ dienen, der mit jeder größeren Version oder bei Bedarf aktualisiert wird.
Auch wenn für Ihr App-Update nur eine neue Patch-Erweiterungsdatei erforderlich ist, müssen Sie trotzdem ein neues APK mit einer 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 mit der Haupterweiterungsdatei identisch. Sie können jede Datei beliebig verwenden.
Dateinamenformat
Sie können jede hochgeladene Erweiterungsdatei in einem beliebigen Format speichern (z. B. ZIP, PDF oder MP4). Sie können auch das Tool JOBB verwenden, um eine Reihe von Ressourcendateien und nachfolgenden Patches für diesen Satz zu kapseln und zu verschlüsseln. Unabhängig vom Dateityp betrachtet Google Play sie als undurchsichtige Binär-Blobs und benennt die Dateien mit dem folgenden Schema um:
[main|patch].<expansion-version>.<package-name>.obb
Dieses Schema besteht aus drei Komponenten:
main
oderpatch
- Gibt an, ob es sich bei der Datei um die Haupt- oder die Patch-Erweiterungsdatei handelt. Pro APK kann es nur eine Hauptdatei und eine Patchdatei geben.
<expansion-version>
- Diese Ganzzahl stimmt mit dem Versionscode des APK überein, mit dem die Erweiterung zuerst verknüpft ist (d. h. mit dem
android:versionCode
-Wert der App).Der Begriff „zuerst“ wird betont, da Sie in der Play Console zwar eine hochgeladene Erweiterungsdatei mit einem neuen APK wiederverwenden können, der Name der Erweiterungsdatei sich jedoch nicht ändert. Sie behält die Version bei, die beim ersten Upload der Datei angewendet wurde.
<package-name>
- Der Java-Paketname Ihrer App.
Angenommen, Ihre APK-Version ist 314159 und Ihr Paketname ist com.beispiel.app. Wenn Sie eine Haupterweiterungsdatei hochladen, wird die Datei in Folgendes umbenannt:
main.314159.com.example.app.obb
Speicherort
Wenn Google Play Ihre Erweiterungsdateien auf ein Gerät herunterlädt, werden sie am freigegebenen Speicherort des Systems gespeichert. Damit die Erweiterungen ordnungsgemäß funktionieren, dürfen Sie sie nicht löschen, verschieben oder umbenennen. Wenn Ihre App den Download von Google Play selbst ausführen muss, müssen Sie die Dateien genau am selben Speicherort speichern.
Die Methode getObbDir()
gibt den Speicherort Ihrer Erweiterungsdateien im folgenden Format zurück:
<shared-storage>/Android/obb/<package-name>/
<shared-storage>
ist der Pfad zum freigegebenen Speicherplatz, der übergetExternalStorageDirectory()
zugänglich ist.<package-name>
ist der Java-Paketname Ihrer App, der untergetPackageName()
verfügbar ist.
Für jede App befinden sich in diesem Verzeichnis nie mehr als zwei Erweiterungsdateien.
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 Berechtigung für den externen Speicher lesen. Für einige Implementierungen von Android 6.0 (API-Level 23) und höher ist jedoch weiterhin eine Berechtigung erforderlich. Sie müssen die Berechtigung READ_EXTERNAL_STORAGE
daher im App-Manifest deklarieren und die Berechtigung zur Laufzeit so anfordern:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Ab Android-Version 6 muss die Berechtigung für den externen Speicher zur Laufzeit angefordert werden. Bei einigen Android-Implementierungen ist jedoch keine Berechtigung zum Lesen von OBB-Dateien erforderlich. Im folgenden Code-Snippet wird gezeigt, 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 danach nicht und speichern Sie die entpackten Daten nicht im selben Verzeichnis. Sie sollten die entpackten Dateien im Verzeichnis speichern, das durch getExternalFilesDir()
angegeben ist. 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 anderen Apps gelesen werden.
Tipp:Wenn du Mediendateien in einer ZIP-Datei verpackst, kannst du die Medienwiedergabe mithilfe von Offset- und Längensteuerungen (z. B. MediaPlayer.setDataSource()
und SoundPool.load()
) auf die Dateien anwenden, ohne die ZIP-Datei entpacken zu müssen. Damit das 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 Dateiendungen anzugeben, 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 dem APK auf das Gerät herunter und speichert sie dort. In einigen Fällen kann Google Play die Erweiterungsdateien jedoch nicht herunterladen oder der Nutzer hat zuvor heruntergeladene Erweiterungsdateien gelöscht. Damit diese Situationen bewältigt werden können, muss Ihre App die Dateien selbst herunterladen können, wenn die Hauptaktivität gestartet wird, und zwar über eine von Google Play bereitgestellte URL.
Der Downloadprozess sieht in etwa so aus:
- Der Nutzer wählt aus, Ihre App über Google Play zu installieren.
- Wenn Google Play die Erweiterungsdateien herunterladen kann (was bei den meisten Geräten der Fall ist), werden sie zusammen mit der 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 einsatzbereit.
- Falls nicht, muss Ihre App die Erweiterungsdateien über HTTP von Google Play herunterladen. Ihre App muss über den App-Lizenzierungsservice von Google Play eine Anfrage an den Google Play-Client senden, die den Namen, die Dateigröße und die URL für jede Erweiterungsdatei enthält. Anhand dieser Informationen können Sie die Dateien herunterladen und an einem geeigneten Speicherort speichern.
Achtung:Sie müssen den erforderlichen Code zum Herunterladen der Erweiterungsdateien von Google Play einfügen, falls die Dateien beim Starten Ihrer App nicht bereits auf dem Gerät vorhanden sind. Wie im folgenden Abschnitt zum Herunterladen der Erweiterungsdateien erläutert, haben wir eine Bibliothek für Sie bereitgestellt, die diesen Vorgang erheblich vereinfacht und den Download von einem Dienst mit minimalem Code ausfü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:
- Prüfen Sie zuerst, ob die komprimierte Downloadgröße Ihrer App mehr als 100 MB betragen muss. Speicherplatz ist kostbar und Sie sollten die Gesamtgröße des Downloads so klein wie möglich halten. Wenn Ihre App mehr als 100 MB benötigt, um mehrere Versionen Ihrer Grafik-Assets für verschiedene Bildschirmdichten bereitzustellen, sollten Sie stattdessen mehrere APKs veröffentlichen, die jeweils nur die für die entsprechenden Bildschirme erforderlichen Assets enthalten. Die besten Ergebnisse bei der Veröffentlichung bei Google Play erzielen Sie, wenn Sie ein Android App Bundle hochladen. Dieses enthält den gesamten kompilierten Code und alle Ressourcen Ihrer App, die Generierung und Signatur des APK wird jedoch an Google Play übergeben.
- Legen Sie fest, welche App-Ressourcen Sie von Ihrem APK trennen möchten, und verpacken Sie sie in einer Datei, die als Haupterweiterungsdatei verwendet werden soll.
Normalerweise sollten Sie die zweite Patch-Erweiterungsdatei nur verwenden, wenn Sie Updates an der Haupterweiterungsdatei ausführen. Wenn Ihre Ressourcen jedoch das Limit von 2 GB für die Haupterweiterungsdatei ü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 freigegebenen Speicherort des Geräts verwendet.
Die Erweiterungsdateien dürfen nicht gelöscht, verschoben oder umbenannt werden.
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 eine Logik hinzu, die beim Starten prüft, ob sich die Erweiterungsdateien auf dem Gerät befinden. Wenn sich die Dateien nicht auf dem Gerät befinden, fordern Sie mit dem App-Lizenzierungsdienst von Google Play URLs für die Erweiterungsdateien an, laden Sie sie herunter und speichern Sie sie.
Um den Codeumfang erheblich zu reduzieren und für eine gute Nutzerfreundlichkeit beim Download zu sorgen, empfehlen wir, die Downloader Library zu verwenden, um das Downloadverhalten zu implementieren.
Wenn Sie anstelle der Bibliothek einen eigenen Downloaddienst erstellen, dürfen Sie den Namen der Erweiterungsdateien nicht ändern und sie müssen am richtigen Speicherort gespeichert werden.
Wenn Sie die App-Entwicklung abgeschlossen haben, folgen Sie der Anleitung zum Testen Ihrer Erweiterungsdateien.
Regeln und Einschränkungen
APK-Erweiterungsdateien können Sie hinzufügen, 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 Nutzer Ihre Erweiterungsdateien von Google Play herunterladen können, müssen sie Ihre App bei Google Play erworben haben. Google Play stellt die URLs für Ihre Erweiterungsdateien nicht bereit, wenn die App auf andere Weise installiert wurde.
- Wenn Sie den Download über Ihre App ausführen, ist die von Google Play für jede Datei bereitgestellte URL für jeden Download eindeutig und läuft kurz nach der Übertragung 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 wird die Version beibehalten, die vom APK empfangen wurde, mit dem die Datei ursprünglich verknüpft war.
- Wenn Sie Erweiterungsdateien in Kombination mit mehreren APKs verwenden, um unterschiedliche Erweiterungsdateien für unterschiedliche Geräte bereitzustellen, müssen Sie trotzdem separate APKs für jedes Gerät hochladen, um einen eindeutigen
versionCode
-Wert anzugeben und für jedes APK unterschiedliche Filter 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 einfach aktualisieren, indem Sie die
versionCode
(und gegebenenfalls auch dieversionName
) ändern. - Speichern Sie keine anderen Daten in Ihrem
obb/
-Verzeichnis. Wenn Sie einige Daten entpacken müssen, speichern Sie sie an dem vongetExternalFilesDir()
angegebenen Speicherort. - Löschen oder benennen Sie die Erweiterungsdatei
.obb
nicht um, es sei denn, Sie führen ein Update durch. Dadurch wird die Erweiterungsdatei von Google Play (oder Ihrer App selbst) wiederholt 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 gleichzeitig mit der Installation oder Aktualisierung der APK-Datei auf das Gerät herunter und speichert sie dort. 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 muss sie sie über eine URL anfordern, die Sie in einer Antwort vom App-Lizenzierungsservice von Google Play erhalten.
Die grundlegende Logik zum Herunterladen Ihrer Erweiterungsdateien ist folgende:
- Suchen Sie beim Starten Ihrer App nach den Erweiterungsdateien im freigegebenen Speicherort (im Verzeichnis
Android/obb/<package-name>/
).- Wenn die Erweiterungsdateien vorhanden sind, ist alles in Ordnung und Ihre App kann fortgesetzt werden.
- Wenn die Erweiterungsdateien nicht vorhanden sind:
- Stellen Sie über die App-Lizenzierung von Google Play eine Anfrage, 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 dabei den genauen Dateinamen verwenden, der in der Antwort von Google Play angegeben ist.Hinweis:Die von Google Play für Ihre Erweiterungsdateien bereitgestellte URL 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 wahrscheinlich den Dienst App-Lizenzierung nicht verwendet. Sie dient in erster Linie dazu, Lizenzierungsrichtlinien für Ihre App durchzusetzen und dafür zu sorgen, dass der Nutzer das Recht hat, Ihre App zu verwenden, da er sie bei Google Play rechtmäßig bezahlt hat. Um die Funktionsweise von Erweiterungsdateien zu vereinfachen, wurde der Lizenzierungsservice erweitert. Er liefert jetzt eine Antwort für Ihre App mit der URL der Erweiterungsdateien Ihrer App, die bei Google Play gehostet werden. Auch wenn Ihre App für Nutzer kostenlos ist, müssen Sie die Lizenzüberprüfungsbibliothek (License Verification Library, LVL) einbinden, um APK-Erweiterungsdateien verwenden zu können. Wenn Ihre App kostenlos ist, müssen Sie die Lizenzbestätigung natürlich nicht erzwingen. Sie benötigen lediglich 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 URLs der Erweiterungsdateien nur zurück, wenn der Nutzer Ihre App bei Google Play erworben hat.
Zusätzlich zum LVL benötigen Sie Code, der die Erweiterungsdateien über eine HTTP-Verbindung herunterlädt und an den richtigen Speicherort im freigegebenen Speicher des Geräts speichert. Wenn Sie diese Funktion in Ihre App einbinden, sollten Sie einige Dinge beachten:
- Auf dem Gerät ist möglicherweise nicht genügend Speicherplatz für die Erweiterungsdateien vorhanden. Sie sollten dies vor Beginn des Downloads prüfen und den Nutzer warnen, 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, Ihre App zu verlassen, während der Download abgeschlossen wird.
- Bei der Anfrage und dem Download können verschiedene Fehler auftreten, die Sie fehlerfrei verarbeiten müssen.
- Die Netzwerkverbindung kann sich während des Downloads ändern. Sie sollten solche Änderungen daher berücksichtigen und den Download bei einer Unterbrechung nach Möglichkeit fortsetzen.
- Während der Download im Hintergrund ausgeführt wird, sollten Sie eine Benachrichtigung anzeigen, die den Fortschritt des Downloads angibt, den Nutzer benachrichtigt, wenn er abgeschlossen ist, und ihn zurück zu Ihrer App leitet, wenn er ausgewählt wird.
Um Ihnen diese Arbeit zu erleichtern, haben wir die Downloader Library entwickelt. Diese ruft die URLs der Erweiterungsdateien über den Lizenzierungsservice ab, lädt die Erweiterungsdateien herunter, führt alle oben aufgeführten Aufgaben aus und ermöglicht es sogar, den Download bei Aktivitäten zu pausieren und fortzusetzen. Wenn Sie Ihrer App die Downloader-Bibliothek und einige Code-Hooks hinzufügen, ist fast der gesamte Code zum Herunterladen der Erweiterungsdateien bereits für Sie codiert. Damit Sie die Erweiterungsdateien mit minimalem Aufwand herunterladen können, empfehlen wir Ihnen, die Downloader Library zu verwenden. In den folgenden Abschnitten wird erläutert, 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 Dokumentation zur App-Lizenzierung folgen, um eine Lizenzanfrage zu stellen, und dann die Namen, Größen und URLs der Erweiterungsdateien aus den Antwort-Extras abrufen. Sie sollten die Klasse APKExpansionPolicy
(in der Lizenzbestätigungsbibliothek enthalten) als Lizenzierungsrichtlinie verwenden. Sie erfasst die Namen, Größen und URLs der Erweiterungsdateien aus dem Lizenzierungsservice.
Downloader-Bibliothek
Wenn Sie APK-Erweiterungsdateien mit Ihrer App verwenden und Nutzern mit minimalem Aufwand eine optimale Nutzererfahrung bieten möchten, empfehlen wir die Verwendung der Downloader Library, die im Paket „Google Play APK Expansion Library“ enthalten ist. Diese Bibliothek lädt Ihre Erweiterungsdateien in einem Hintergrunddienst herunter, zeigt eine Nutzerbenachrichtigung mit dem Downloadstatus an, verarbeitet den Verlust der Netzwerkverbindung und setzt den Download nach Möglichkeit fort.
So implementieren Sie Downloads von Erweiterungsdateien mit der Downloader Library:
- Erweitern Sie eine spezielle
Service
-Unterklasse und eine spezielleBroadcastReceiver
-Unterklasse, für die jeweils nur wenige Codezeilen erforderlich sind. - Fügen Sie Ihrer Hauptaktivität eine Logik hinzu, die prüft, ob die Erweiterungsdateien bereits heruntergeladen wurden. Falls nicht, wird der Downloadvorgang aufgerufen und eine Fortschritts-UI angezeigt.
- Implementieren Sie in Ihrer Hauptaktivität eine Rückrufschnittstelle mit einigen Methoden, die Aktualisierungen zum Downloadfortschritt erhält.
In den folgenden Abschnitten wird beschrieben, wie du deine App mit der Downloader Library einrichtest.
Downloader-Bibliothek verwenden
Wenn Sie die Downloader-Bibliothek verwenden möchten, müssen Sie zwei Pakete aus dem SDK-Manager herunterladen und Ihrer App die entsprechenden Bibliotheken hinzufügen.
Öffnen Sie zuerst den Android SDK Manager (Tools > SDK Manager) und wählen Sie unter Darstellung und Verhalten > Systemeinstellungen > 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 Downloaderbibliothek. 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 Namen für die App/Bibliothek wie „Google Play License Library“ und „Google Play Downloader Library“ an, wählen Sie Minimum SDK level (Mindest-SDK-Ebene) und dann Finish (Fertigstellen) aus.
- Wählen Sie Datei > Projektstruktur aus.
- Wähle den Tab Eigenschaften aus und gib unter Bibliotheks-Repository den Pfad zur Bibliothek im Verzeichnis
<sdk>/extras/google/
ein (play_licensing/
für die Bibliothek zur Lizenzüberprüfung 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 Lizenzbestätigungsbibliothek ab. Fügen Sie die Bibliothek zur Lizenzüberprüfung den Projekteigenschaften der Downloader-Bibliothek hinzu.
Sie können Ihr Projekt auch über eine Befehlszeile aktualisieren, um die Bibliotheken einzubinden:
- Wechseln Sie in das Verzeichnis
<sdk>/tools/
. - Führe
android update project
mit der Option--library
aus, um deinem Projekt sowohl die LVL- als auch die Downloader-Bibliothek 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 License Verification Library als auch die Downloader Library hinzugefügt haben, können Sie schnell die Möglichkeit zum Herunterladen von Erweiterungsdateien von Google Play einbinden. Das Format, das Sie für die Erweiterungsdateien auswählen, und die Art und Weise, wie Sie sie aus dem freigegebenen Speicher lesen, sind separate Implementierungen, die Sie je nach den Anforderungen Ihrer App berücksichtigen sollten.
Tipp:Das APK Expansion-Paket enthält eine Beispiel-App, die zeigt, wie die Downloader-Bibliothek in einer App verwendet wird. Das Beispiel verwendet eine Drittanbieterbibliothek, die im APK Expansion-Paket als APK Expansion Zip Library verfügbar ist. 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 unten zum Verwenden der APK-Erweiterungs-ZIP-Bibliothek.
Nutzerberechtigungen deklarieren
Zum Herunterladen der Erweiterungsdateien benötigt die Downloader Library mehrere Berechtigungen, die Sie in der Manifestdatei Ihrer App angeben müssen. Sie 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:Für die Downloader-Bibliothek ist standardmäßig API-Level 4 erforderlich, für die APK-Erweiterungs-Zip-Bibliothek jedoch API-Level 5.
Downloaderdienst implementieren
Um Downloads im Hintergrund auszuführen, bietet die Downloader Library eine eigene Service
-Unterklasse namens DownloaderService
, die Sie erweitern sollten. Neben dem Herunterladen der Erweiterungsdateien für Sie bietet die DownloaderService
noch folgende Vorteile:
- Registriert einen
BroadcastReceiver
, der auf Änderungen an der Netzwerkverbindung des Geräts (CONNECTIVITY_ACTION
-Broadcast) achtet, um den Download bei Bedarf anzuhalten (z. B. bei Verbindungsverlust) und ihn fortzusetzen, sobald eine Verbindung hergestellt wurde. - Es wird ein
RTC_WAKEUP
-Alarm geplant, um den Download bei Beendigung des Dienstes noch einmal zu versuchen. - Erstellt eine benutzerdefinierte
Notification
, in der der Downloadfortschritt und alle Fehler oder Statusänderungen angezeigt werden. - Ermöglicht es Ihrer App, den Download manuell anzuhalten und fortzusetzen.
- Prüft, ob der freigegebene Speicher bereitgestellt und verfügbar ist, ob die Dateien noch nicht vorhanden sind und ob genügend Speicherplatz vorhanden ist, bevor die Erweiterungsdateien heruntergeladen werden. Anschließend wird der Nutzer benachrichtigt, wenn eine dieser Angaben nicht zutrifft.
Sie müssen lediglich eine Klasse in Ihrer App erstellen, die die Klasse DownloaderService
erweitert, und drei Methoden überschreiben, um bestimmte App-Details anzugeben:
getPublicKey()
- Dabei muss ein String zurückgegeben werden, der der Base64-codierte RSA-Public-Key für Ihr Publisher-Konto ist. Diesen finden Sie auf der Profilseite in der Play Console (siehe Einrichtung für die Lizenzierung).
getSALT()
- Dabei muss ein Array von Zufallsbytes zurückgegeben werden, das die Lizenzierungs-
Policy
zum Erstellen einerObfuscator
verwendet. Das Salt sorgt dafür, dass Ihre verschleierteSharedPreferences
-Datei, in der Ihre Lizenzierungsdaten gespeichert sind, eindeutig und nicht auffindbar ist. getAlarmReceiverClassName()
- Dabei muss der Klassenname der
BroadcastReceiver
in Ihrer App zurückgegeben werden, die die Benachrichtigung erhalten soll, dass der Download fortgesetzt werden soll. Dies kann passieren, wenn der Downloaddienst 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:Du musst den Wert für BASE64_PUBLIC_KEY
auf den öffentlichen Schlüssel aktualisieren, der zu deinem Publisher-Konto gehört. Sie finden den Schlüssel in der Developers Console unter Ihren Profilinformationen. Das ist auch beim Testen Ihrer Downloads erforderlich.
Deklarieren Sie den Dienst in Ihrer Manifestdatei:
<app ...> <service android:name=".SampleDownloaderService" /> ... </app>
Weckerempfänger implementieren
Um den Fortschritt der Dateidownloads zu überwachen und den Download bei Bedarf neu zu starten, plant die DownloaderService
einen RTC_WAKEUP
-Alarm, der eine Intent
an eine BroadcastReceiver
in Ihrer App sendet. Sie müssen die BroadcastReceiver
so definieren, dass eine API aus der Downloader-Bibliothek aufgerufen wird, die den Status des Downloads prüft und ihn bei Bedarf neu startet.
Sie müssen dazu 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(); } } }
Beachten Sie, dass dies die Klasse ist, für die Sie den Namen in der getAlarmReceiverClassName()
-Methode Ihres Dienstes zurückgeben müssen (siehe vorherigen Abschnitt).
Deklarieren Sie den Empfänger in Ihrer Manifestdatei:
<app ...> <receiver android:name=".SampleAlarmReceiver" /> ... </app>
Download starten
Die Hauptaktivität in Ihrer App (diejenige, die über Ihr 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, falls nicht.
Für den Start des Downloads mit der Downloader Library sind folgende Schritte erforderlich:
- Prüfen Sie, ob die Dateien heruntergeladen wurden.
Die Downloader Library enthält einige APIs in der Klasse
Helper
, die bei diesem Vorgang helfen:getExpansionAPKFileName(Context, c, boolean mainFile, int versionCode)
doesFileExist(Context c, String fileName, long fileSize)
In der im Apk-Erweiterungspaket enthaltenen Beispiel-App wird beispielsweise in der
onCreate()
-Methode der Aktivität die folgende Methode 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 die 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 mit dem Download beginnen.
- Starten Sie den Download durch Aufrufen der statischen Methode
DownloaderClientMarshaller.startDownloadServiceIfRequired(Context c, PendingIntent notificationClient, Class<?> serviceClass)
.Die Methode nimmt folgende Parameter an:
context
: DerContext
Ihrer App.notificationClient
: EinePendingIntent
, um Ihre Hauptaktivität zu starten. Dieser wird in derNotification
verwendet, die von derDownloaderService
erstellt wird, um den Downloadfortschritt anzuzeigen. Wenn der Nutzer die Benachrichtigung auswählt, ruft das System die von Ihnen hier angegebenePendingIntent
auf und sollte die Aktivität öffnen, in der der Downloadfortschritt angezeigt wird (normalerweise dieselbe Aktivität, mit der der Download gestartet wurde).serviceClass
: DasClass
-Objekt für deine Implementierung vonDownloaderService
, das zum Starten des Dienstes und bei Bedarf zum Starten des Downloads erforderlich ist.
Die Methode gibt eine Ganzzahl zurück, die angibt, ob der Download erforderlich ist. Mögliche Werte sind:
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 Lizenzbestätigung erforderlich ist, um die URLs der Erweiterungsdateien abzurufen.DOWNLOAD_REQUIRED
: Wird zurückgegeben, wenn die URLs der Erweiterungsdateien bereits bekannt, aber noch nicht heruntergeladen wurden.
Das Verhalten von
LVL_CHECK_REQUIRED
undDOWNLOAD_REQUIRED
ist im Wesentlichen dasselbe und Sie müssen sich normalerweise keine Gedanken darüber machen. In der Hauptaktivität, in derstartDownloadServiceIfRequired()
aufgerufen wird, kannst du einfach prüfen, ob die AntwortNO_DOWNLOAD_REQUIRED
ist. Wenn die Antwort nichtNO_DOWNLOAD_REQUIRED
lautet, beginnt die Downloader Library mit dem Download. Du solltest dann die Aktivitäts-UI aktualisieren, um den Downloadfortschritt anzuzeigen (siehe nächster Schritt). Wenn die AntwortNO_DOWNLOAD_REQUIRED
ist, 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_REQUIRED
zurückgibt, erstellen Sie eine Instanz vonIStub
, indem SieDownloaderClientMarshaller.CreateStub(IDownloaderClient client, Class<?> downloaderService)
aufrufen. DieIStub
stellt eine Bindung zwischen deiner Aktivität und dem Downloaddienst her, sodass deine Aktivität Rückrufe zum Downloadfortschritt erhält.Wenn Sie
IStub
durch Aufrufen vonCreateStub()
instanziieren möchten, müssen Sie ihm eine Implementierung derIDownloaderClient
-Schnittstelle und IhreDownloaderService
-Implementierung übergeben. Im nächsten Abschnitt zum Empfangen des Downloadfortschritts wird dieIDownloaderClient
-Schnittstelle beschrieben. Sie sollten sie normalerweise in IhrerActivity
-Klasse implementieren, damit Sie die Aktivitäts-UI aktualisieren können, wenn sich der Downloadstatus ändert.Wir empfehlen,
CreateStub()
aufzurufen, umIStub
während deronCreate()
-Methode Ihrer Aktivität zu instanziieren, nachdemstartDownloadServiceIfRequired()
den Download gestartet hat.Im vorherigen Codebeispiel für
onCreate()
können Sie beispielsweise so auf dasstartDownloadServiceIfRequired()
-Ergebnis 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 für Ihre AktivitätonResume()
aufgerufen. Dort sollten Sie dannconnect()
auf derIStub
aufrufen und ihr 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(); }
Wenn du
connect()
auf derIStub
aufrufst, wird deine Aktivität an dieDownloaderService
gebunden, sodass deine Aktivität über dieIDownloaderClient
-Schnittstelle Rückrufe zu Änderungen am Downloadstatus erhält.
Downloadfortschritt
Wenn du Updates zum Downloadfortschritt erhalten und mit dem DownloaderService
interagieren möchtest, musst du die IDownloaderClient
-Oberfläche 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 du die
IStub
in deiner Aktivität instanziiert hast, wird diese Methode aufgerufen. Dabei wird einMessenger
-Objekt übergeben, das mit deiner Instanz vonDownloaderService
verknüpft ist. Wenn du Anfragen an den Dienst senden möchtest, z. B. um Downloads anzuhalten und fortzusetzen, musst duDownloaderServiceMarshaller.CreateProxy()
aufrufen, um die mit dem Dienst verbundeneIDownloaderService
-Benutzeroberfläche 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 dem Downloaddienst Befehle senden, z. B. um den Download anzuhalten und fortzusetzen (requestPauseDownload()
undrequestContinueDownload()
). onDownloadStateChanged(int newState)
- Der Downloaddienst ruft diese Funktion auf, wenn sich der Downloadstatus ändert, z. B. wenn der Download beginnt oder abgeschlossen wird.
Der Wert von
newState
ist einer von mehreren möglichen Werten, die in einer derSTATE_*
-Konstanten der KlasseIDownloaderClient
angegeben sind.Wenn Sie Ihren Nutzern eine nützliche Nachricht anzeigen möchten, können Sie für jeden Status einen entsprechenden String anfordern, indem Sie
Helpers.getDownloaderStringResourceIDFromState()
aufrufen. Dadurch wird die Ressourcen-ID für einen der Strings zurückgegeben, die in der Downloader-Bibliothek enthalten sind. Der String „Download pausiert, da Sie im Roaming sind“ entspricht beispielsweiseSTATE_PAUSED_ROAMING
. onDownloadProgress(DownloadProgressInfo progress)
- Der Downloaddienst ruft diese auf, um ein
DownloadProgressInfo
-Objekt zu senden, das verschiedene Informationen zum Downloadfortschritt enthält, einschließlich der geschätzten verbleibenden Zeit, der aktuellen Geschwindigkeit, des Gesamtfortschritts und des Gesamtwerts, damit du die Benutzeroberfläche für den Downloadfortschritt aktualisieren kannst.
Tipp:Beispiele für diese Rückrufe, die die Benutzeroberfläche für den Downloadfortschritt aktualisieren, finden Sie in der Beispiel-App im Apk-Erweiterungspaket unter SampleDownloaderActivity
.
Einige öffentliche Methoden für die IDownloaderService
-Benutzeroberfläche, die für Sie nützlich sein könnten:
requestPauseDownload()
- Pausiert den Download.
requestContinueDownload()
- Pausierten Download fortsetzen.
setDownloadFlags(int flags)
- Legt die Nutzereinstellungen für Netzwerktypen fest, von denen Dateien heruntergeladen werden dürfen. Die aktuelle Implementierung unterstützt ein Flag,
FLAGS_DOWNLOAD_OVER_CELLULAR
, Sie können aber weitere hinzufügen. Standardmäßig ist dieses Flag deaktiviert. Der Nutzer muss also mit einem WLAN verbunden sein, um Erweiterungsdateien herunterzuladen. Sie können eine Nutzereinstellung bereitstellen, um Downloads über das Mobilfunknetz zu ermöglichen. In diesem Fall können Sie sich an folgende Stellen wenden:Kotlin
remoteService = DownloaderServiceMarshaller.CreateProxy(m).apply { ... setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR) }
Java
remoteService .setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);
APKExpansionPolicy verwenden
Wenn Sie sich dafür entscheiden, einen eigenen Downloaddienst zu erstellen, anstatt die Downloader-Bibliothek von Google Play zu verwenden, sollten Sie trotzdem die APKExpansionPolicy
verwenden, die in der Lizenzbestätigungsbibliothek bereitgestellt wird. Die Klasse APKExpansionPolicy
ist fast identisch mit ServerManagedPolicy
(verfügbar in der Google Play License Verification Library), enthält aber eine zusätzliche Verarbeitung für die Antwort-Extras der APK-Erweiterungsdatei.
Hinweis:Wenn du die Downloader Library wie im vorherigen Abschnitt beschrieben verwendest, führt die Bibliothek alle Interaktionen mit der APKExpansionPolicy
aus. Du musst 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 nicht die Downloader Library verwenden, finden Sie in der Dokumentation zum Hinzufügen von Lizenzen zu Ihrer App. Dort wird erläutert, wie Sie eine Lizenzrichtlinie wie diese implementieren.
Erweiterungsdatei lesen
Sobald Ihre APK-Erweiterungsdateien auf dem Gerät gespeichert sind, hängt die Art und Weise, wie Sie sie lesen, vom verwendeten Dateityp ab. Wie in der Übersicht erläutert, können Ihre Erweiterungsdateien beliebige Dateien 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. Möglicherweise hat der Nutzer den Speicher über USB auf einem Computer bereitgestellt oder die SD-Karte entfernt.
Hinweis:Beim Starten Ihrer App sollten Sie immer prüfen, ob der externe Speicherplatz verfügbar und lesbar ist. Rufen Sie dazu getExternalStorageState()
auf. Dies gibt einen von mehreren möglichen Strings zurück, die den Status des externen Speichers darstellen. Damit er von Ihrer App gelesen werden kann, muss der Rückgabewert MEDIA_MOUNTED
sein.
Dateinamen abrufen
Wie in der Übersicht beschrieben, werden Ihre APK-Erweiterungsdateien in einem bestimmten Dateinamenformat gespeichert:
[main|patch].<expansion-version>.<package-name>.obb
Um den Speicherort und die Namen Ihrer Erweiterungsdateien zu ermitteln, sollten Sie die Methoden getExternalStorageDirectory()
und getPackageName()
verwenden, um den Pfad zu Ihren Dateien zu erstellen.
Mit dieser Methode können Sie in Ihrer App ein Array mit dem vollständigen Pfad zu beiden Erweiterungsdateien abrufen:
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
-Datei Ihrer App und die Version der gewünschten Erweiterungsdatei übergeben.
Es gibt viele Möglichkeiten, die Versionsnummer der Erweiterungsdatei zu ermitteln. Eine einfache Möglichkeit besteht darin, die Version zu Beginn des Downloads in einer SharedPreferences
-Datei zu speichern. Dazu wird der Name der Erweiterungsdatei mit der getExpansionFileName(int index)
-Methode der APKExpansionPolicy
-Klasse abgefragt. Wenn Sie auf die Erweiterungsdatei zugreifen möchten, können Sie den Versionscode aus der SharedPreferences
-Datei lesen.
Weitere Informationen zum Lesen aus dem freigegebenen Speicher finden Sie in der Dokumentation zum Datenspeicher.
APK-Erweiterungs-Zip-Bibliothek verwenden
Das Google Market Apk Expansion-Paket enthält eine Bibliothek namens APK Expansion Zip Library (in <sdk>/extras/google/google_market_apk_expansion/zip_file/
). Dies ist eine optionale Bibliothek, mit der Sie Ihre Erweiterungsdateien lesen können, 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-Erweiterungs-Zip-Bibliothek enthält die folgenden Klassen und APIs:
APKExpansionSupport
- Bietet einige Methoden zum Zugriff auf Erweiterungsdateinamen und ZIP-Dateien:
getAPKExpansionFiles()
- Die oben beschriebene Methode, die den vollständigen Dateipfad zu beiden Erweiterungsdateien zurückgibt.
getAPKExpansionZipFile(Context ctx, int mainVersion, int patchVersion)
- Gibt eine
ZipResourceFile
zurück, die die Summe der Hauptdatei und der Patchdatei darstellt. Wenn Sie also sowohlmainVersion
als auchpatchVersion
angeben, wird eineZipResourceFile
zurückgegeben, die Lesezugriff auf alle Daten gewährt. Dabei werden die Daten der Patchdatei mit den Daten der Hauptdatei zusammengeführt.
ZipResourceFile
- Stellt eine ZIP-Datei im freigegebenen Speicher dar und führt alle Aufgaben aus, um ein virtuelles Dateisystem auf der Grundlage Ihrer ZIP-Dateien bereitzustellen. Sie können eine Instanz mit
APKExpansionSupport.getAPKExpansionZipFile()
oderZipResourceFile
abrufen, indem Sie den Pfad zu Ihrer Erweiterungsdatei übergeben. Diese Klasse enthält eine Vielzahl nützlicher Methoden, auf die Sie in der Regel jedoch nicht zugreifen müssen. Hier einige wichtige Methoden:getInputStream(String assetPath)
- Bietet einen
InputStream
zum Lesen einer Datei in der ZIP-Datei.assetPath
muss der Pfad zur gewünschten Datei sein, bezogen auf das Stammverzeichnis des ZIP-Dateiinhalts. getAssetFileDescriptor(String assetPath)
- Gibt einen
AssetFileDescriptor
für eine Datei in der ZIP-Datei an.assetPath
muss der Pfad zur gewünschten Datei sein, relativ zum Stammverzeichnis des ZIP-Dateiinhalts. Dies ist nützlich für bestimmte Android-APIs, für die eineAssetFileDescriptor
erforderlich ist, 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 InhaltsanbieterUri
verwaltet, um Dateizugriff für bestimmte Android APIs bereitzustellen, dieUri
-Zugriff auf Mediendateien erfordern. 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 Mediendateien verwenden, können Sie mit einer ZIP-Datei weiterhin Android-Aufrufe zur Medienwiedergabe verwenden, die Offset- und Längensteuerungen bieten (z. B. MediaPlayer.setDataSource()
und SoundPool.load()
). Damit dies funktioniert, dürfen Sie die Mediendateien beim Erstellen der ZIP-Pakete nicht zusätzlich komprimieren. Wenn Sie beispielsweise das zip
-Tool verwenden, sollten Sie die Option -n
verwenden, um die Dateiendungen 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, ist für das 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 obige Code bietet Zugriff auf alle Dateien, die entweder in der Haupterweiterungsdatei oder in der Patch-Erweiterungsdatei vorhanden sind. Dazu wird aus einer zusammengeführten Zuordnung aller Dateien aus beiden Dateien gelesen. Für die getAPKExpansionFile()
-Methode müssen Sie nur Ihre App-android.content.Context
und die Versionsnummer sowohl für die Haupterweiterungsdatei als auch für die Patcherweiterungsdatei angeben.
Wenn du lieber aus einer bestimmten Erweiterungsdatei lesen möchtest, kannst du 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 SampleDownloaderActivity
-Klasse der Beispiel-App. Sie enthält zusätzlichen Code zum Verifizieren der heruntergeladenen Dateien mithilfe des CRC. Wenn Sie dieses Beispiel als Grundlage für Ihre eigene Implementierung verwenden, müssen Sie die Bytegröße Ihrer Erweiterungsdateien im xAPKS
-Array angeben.
Erweiterungsdateien testen
Bevor Sie Ihre App veröffentlichen, sollten Sie zwei Dinge testen: das Lesen der Erweiterungsdateien und das Herunterladen der Dateien.
Dateilesevorgänge testen
Bevor Sie Ihre App bei Google Play hochladen, sollten Sie testen, ob Ihre App die Dateien aus dem freigegebenen Speicher lesen kann. Sie müssen die Dateien nur an den entsprechenden Speicherort im freigegebenen Speicher des Geräts platzieren und die App starten:
- Erstellen Sie auf Ihrem Gerät das entsprechende Verzeichnis im freigegebenen Speicher, in dem Google Play Ihre Dateien speichert.
Wenn der Paketname beispielsweise
com.example.android
lautet, 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 diesem Verzeichnis manuell hinzu. Benennen Sie Ihre Dateien so um, dass sie dem Dateinamenformat entsprechen, das von Google Play verwendet wird.
Unabhängig vom Dateityp sollte die Haupterweiterungsdatei für die
com.example.android
-App beispielsweisemain.0300110.com.example.android.obb
heißen. Der Versionscode kann beliebig sein. Beachte dabei Folgendes:- Die Haupterweiterungsdatei beginnt immer mit
main
und die Patchdatei mitpatch
. - Der Paketname stimmt immer mit dem des APK überein, an das die Datei bei Google Play angehängt ist.
- Die Haupterweiterungsdatei beginnt immer mit
- Nachdem die Erweiterungsdatei(en) auf dem Gerät sind, können Sie die App installieren und ausführen, um die Erweiterungsdatei(en) zu testen.
Hier sind einige Hinweise zur Verwendung der Erweiterungsdateien:
- Löschen oder umbenennen Sie die
.obb
-Erweiterungsdateien nicht, auch wenn Sie die Daten an einem anderen Speicherort entpacken. Dadurch wird die Erweiterungsdatei von Google Play (oder Ihrer App selbst) wiederholt heruntergeladen. - Speichern Sie keine anderen Daten in Ihrem
obb/
-Verzeichnis. Wenn Sie einige Daten entpacken müssen, speichern Sie sie an dem vongetExternalFilesDir()
angegebenen Speicherort.
Dateidownloads testen
Da Ihre App die Erweiterungsdateien beim ersten Öffnen manchmal manuell herunterladen muss, sollten Sie diesen Vorgang testen, um sicherzustellen, dass Ihre App die URLs abfragen, die Dateien herunterladen und auf dem Gerät speichern kann.
Wenn Sie die Implementierung des manuellen Downloadvorgangs Ihrer App testen möchten, können Sie sie im internen Test-Track veröffentlichen, sodass sie nur für autorisierte Tester verfügbar ist. Wenn alles wie erwartet funktioniert, sollte Ihre App mit dem Herunterladen der Erweiterungsdateien beginnen, 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 Vorabversionen von Apps werden nicht mehr unterstützt.
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 herunterzuladen. Da Sie bei Google Play für jedes APK zwei Erweiterungsdateien angeben können, können Sie die zweite Datei als „Patch“ verwenden, der Updates und neue Assets enthält. So muss die Haupterweiterungsdatei nicht noch einmal heruntergeladen werden, was für Nutzer sehr zeitaufwendig und teuer sein kann.
Die Patch-Erweiterungsdatei ist technisch mit der Haupterweiterungsdatei identisch. Weder das Android-System noch Google Play führen tatsächliche Patches zwischen der Haupt- und der Patch-Erweiterungsdatei durch. Alle erforderlichen Patches müssen vom App-Code selbst ausgeführt werden.
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 die 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 Haupterweiterungsdatei, die mit dem APK verknüpft ist, in der Play Console nicht ändern, laden Nutzer, die Ihre App bereits installiert haben, die Haupterweiterungsdatei nicht herunter. Bestehende Nutzer erhalten nur das aktualisierte APK und die neue Patch-Erweiterungsdatei. Die vorherige Haupterweiterungsdatei bleibt erhalten.
Beachten Sie bei Updates von Erweiterungsdateien Folgendes:
- Es kann jeweils nur zwei Erweiterungsdateien für Ihre App geben. Eine Haupterweiterungsdatei und eine Patcherweiterungsdatei. Bei der Aktualisierung einer Datei löscht Google Play die vorherige Version. Das muss auch bei manuellen Updates in Ihrer App geschehen.
- Wenn Sie eine Patch-Erweiterungsdatei hinzufügen, wird Ihre App oder Haupterweiterungsdatei vom Android-System nicht gepatcht. Sie müssen Ihre App so entwickeln, dass sie die Patch-Daten unterstützt. Das Apk-Erweiterungspaket enthält jedoch eine Bibliothek für die Verwendung von ZIP-Dateien als Erweiterungsdateien, die die Daten aus der Patchdatei in die Haupterweiterungsdatei zusammenführt, sodass Sie alle Daten der Erweiterungsdatei problemlos lesen können.