Asset Delivery einbinden (Kotlin und Java)

Folgen Sie der Anleitung in diesem Leitfaden, um über Ihren Java-Code auf die Asset-Packs Ihrer App zuzugreifen.

Für Kotlin und Java erstellen

Führen Sie die folgenden Schritte aus, um die Play Asset Delivery in das Android-App-Bundle Ihres Projekts einzubinden. Sie müssen dazu nicht Android Studio verwenden.

  1. Aktualisieren Sie die Version des Android-Gradle-Plug-ins in der Datei build.gradle Ihres Projekts auf 4.0.0 oder höher.

  2. Erstellen Sie im obersten Verzeichnis Ihres Projekts ein Verzeichnis für das Asset-Paket. Dieser Verzeichnisname wird als Name des Asset-Pakets verwendet. Asset-Pack-Namen müssen mit einem Buchstaben beginnen und dürfen nur Buchstaben, Zahlen und Unterstriche enthalten.

  3. Erstellen Sie im Verzeichnis des Asset-Pakets eine build.gradle-Datei und fügen Sie den folgenden Code hinzu. Gib den Namen des Asset-Pakets und nur einen Übermittlungstyp an:

    Groovy

    // In the asset pack's build.gradle file:
    plugins {
      id 'com.android.asset-pack'
    }
    
    assetPack {
        packName = "asset-pack-name" // Directory name for the asset pack
        dynamicDelivery {
            deliveryType = "[ install-time | fast-follow | on-demand ]"
        }
    }
    

    Kotlin

    // In the asset pack's build.gradle.kts file:
    plugins {
      id("com.android.asset-pack")
    }
    
    assetPack {
      packName.set("asset-pack-name") // Directory name for the asset pack
      dynamicDelivery {
        deliveryType.set("[ install-time | fast-follow | on-demand ]")
      }
    }
    
  4. Fügen Sie in der build.gradle-Datei der App des Projekts den Namen jedes Asset-Pakets in Ihrem Projekt hinzu, wie unten gezeigt:

    Groovy

    // In the app build.gradle file:
    android {
        ...
        assetPacks = [":asset-pack-name", ":asset-pack2-name"]
    }
    

    Kotlin

    // In the app build.gradle.kts file:
    android {
        ...
        assetPacks += listOf(":asset-pack-name", ":asset-pack2-name")
    }
    
  5. Fügen Sie in der settings.gradle-Datei des Projekts alle Asset-Pakete in Ihr Projekt ein, wie unten gezeigt:

    Groovy

    // In the settings.gradle file:
    include ':app'
    include ':asset-pack-name'
    include ':asset-pack2-name'
    

    Kotlin

    // In the settings.gradle.kts file:
    include(":app")
    include(":asset-pack-name")
    include(":asset-pack2-name")
    
  6. Erstellen Sie im Verzeichnis des Asset-Pakets das folgende Unterverzeichnis: src/main/assets.

  7. Lege die Assets im Verzeichnis src/main/assets ab. Hier können Sie auch Unterverzeichnisse erstellen. Die Verzeichnisstruktur Ihrer App sollte jetzt so aussehen:

    • build.gradle
    • settings.gradle
    • app/
    • asset-pack-name/build.gradle
    • asset-pack-name/src/main/assets/your-asset-directories
  8. Erstellen Sie das Android App Bundle mit Gradle. Im generierten App-Bundle enthält das Verzeichnis auf Stammebene jetzt Folgendes:

    • asset-pack-name/manifest/AndroidManifest.xml: Konfiguration der Kennung und des Bereitstellungsmodus des Asset-Pakets
    • asset-pack-name/assets/your-asset-directories: Verzeichnis mit allen Assets, die im Asset-Paket bereitgestellt werden

    Gradle generiert das Manifest für jedes Asset-Paket und gibt das assets/-Verzeichnis für Sie aus.

  9. Optional: Füge die Play Asset Delivery Library hinzu, wenn du die Fast-Follow- und On-Demand-Auslieferung verwenden möchtest.

    Groovy

    implementation "com.google.android.play:asset-delivery:2.2.2"
    // For Kotlin use asset-delivery-ktx
    implementation "com.google.android.play:asset-delivery-ktx:2.2.2"
    

    Kotlin

    implementation("com.google.android.play:asset-delivery:2.2.2")
    // For Kotlin use core-ktx
    implementation("com.google.android.play:asset-delivery-ktx:2.2.2")
    

  10. Optional: Konfigurieren Sie Ihr App-Bundle so, dass verschiedene Texturkomprimierungsformate unterstützt werden.

Play Asset Delivery API einbinden

Die Play Asset Delivery Java API bietet die Klasse AssetPackManager zum Anfordern von Asset-Packs, zum Verwalten von Downloads und zum Zugriff auf die Assets. Fügen Sie Ihrem Projekt zuerst die Play Asset Delivery Library hinzu.

Sie implementieren diese API entsprechend dem Bereitstellungstyp des Asset-Pakets, auf das Sie zugreifen möchten. Diese Schritte sind im folgenden Flussdiagramm dargestellt.

Asset-Paket-Flussdiagramm für die Programmiersprache Java

Abbildung 1. Flussdiagramm für den Zugriff auf Asset-Pakete

Bereitstellung bei der Installation

Asset-Pakete, die als install-time konfiguriert sind, sind beim Starten der App sofort verfügbar. Verwenden Sie die Java-AssetManager API, um auf Assets zuzugreifen, die in diesem Modus bereitgestellt werden:

Kotlin

import android.content.res.AssetManager
...
val context: Context = createPackageContext("com.example.app", 0)
val assetManager: AssetManager = context.assets
val stream: InputStream = assetManager.open("asset-name")

Java

import android.content.res.AssetManager;
...
Context context = createPackageContext("com.example.app", 0);
AssetManager assetManager = context.getAssets();
InputStream is = assetManager.open("asset-name");

Fast-Follow- und On-Demand-Auslieferung

In den folgenden Abschnitten erfahren Sie, wie Sie Informationen zu Asset-Packs abrufen, bevor Sie sie herunterladen, wie Sie die API aufrufen, um den Download zu starten, und wie Sie auf die heruntergeladenen Pakete zugreifen. Diese Abschnitte gelten für fast-follow- und on-demand-Asset-Packs.

Status prüfen

Jedes Asset-Paket wird in einem separaten Ordner im internen Speicher der App gespeichert. Mit der Methode getPackLocation() kannst du den Stammordner eines Asset-Pakets ermitteln. Diese Methode gibt die folgenden Werte zurück:

Rückgabewert Status
Ein gültiges AssetPackLocation-Objekt Der Stammordner des Asset-Pakets ist unter assetsPath() sofort zugänglich.
null Unbekanntes Asset-Paket oder Assets sind nicht verfügbar

Downloadinformationen zu Asset-Packs abrufen

Apps müssen die Größe des Downloads offenlegen, bevor das Asset-Paket abgerufen wird. Mit der Methode requestPackStates() oder getPackStates() kannst du die Größe des Downloads und ob das Paket bereits heruntergeladen wird, ermitteln.

Kotlin

suspend fun requestPackStates(packNames: List<String>): AssetPackStates

Java

Task<AssetPackStates> getPackStates(List<String> packNames)

requestPackStates() ist eine Pausierfunktion, die ein AssetPackStates-Objekt zurückgibt. getPackStates() ist eine asynchrone Methode, die ein Task<AssetPackStates> zurückgibt. Die Methode packStates() eines AssetPackStates-Objekts gibt einen Map<String, AssetPackState> zurück. Diese Zuordnung enthält den Status jedes angeforderten Asset-Pakets, sortiert nach Name:

Kotlin

AssetPackStates#packStates(): Map<String, AssetPackState>

Java

Map<String, AssetPackState> AssetPackStates#packStates()

Die endgültige Anfrage sieht so aus:

Kotlin

const val assetPackName = "assetPackName"
coroutineScope.launch {
  try {
    val assetPackStates: AssetPackStates =
      manager.requestPackStates(listOf(assetPackName))
    val assetPackState: AssetPackState =
      assetPackStates.packStates()[assetPackName]
  } catch (e: RuntimeExecutionException) {
    Log.d("MainActivity", e.message)
  }
}

Java

final String assetPackName = "myasset";

assetPackManager
    .getPackStates(Collections.singletonList(assetPackName))
    .addOnCompleteListener(new OnCompleteListener<AssetPackStates>() {
        @Override
        public void onComplete(Task<AssetPackStates> task) {
            AssetPackStates assetPackStates;
            try {
                assetPackStates = task.getResult();
                AssetPackState assetPackState =
                    assetPackStates.packStates().get(assetPackName);
            } catch (RuntimeExecutionException e) {
                Log.d("MainActivity", e.getMessage());
                return;
            })

Die folgenden AssetPackState-Methoden geben die Größe des Asset-Packs, die bisher heruntergeladene Menge (falls angefordert) und die bereits in die App übertragene Menge an:

Um den Status eines Asset-Pakets abzurufen, verwende die Methode status(). Sie gibt den Status als Ganzzahl zurück, die einem Konstantenfeld in der Klasse AssetPackStatus entspricht. Ein Asset-Paket, das noch nicht installiert ist, hat den Status AssetPackStatus.NOT_INSTALLED.

Wenn eine Anfrage fehlschlägt, verwende die Methode errorCode(), deren Rückgabewert einem konstanten Feld in der Klasse AssetPackErrorCode entspricht.

Installieren

Verwende die Methode requestFetch() oder fetch(), um ein Asset-Paket zum ersten Mal herunterzuladen oder das Update eines Asset-Pakets abzuschließen:

Kotlin

suspend fun AssetPackManager.requestFetch(packs: List<String>): AssetPackStates

Java

Task<AssetPackStates> fetch(List<String> packNames)

Diese Methode gibt ein AssetPackStates-Objekt zurück, das eine Liste von Paketen und deren ursprünglichen Downloadstatus und -größen enthält. Wenn ein über requestFetch() oder fetch() angefordertes Asset-Paket bereits heruntergeladen wird, wird der Downloadstatus zurückgegeben und kein zusätzlicher Download gestartet.

Downloadstatus überwachen

Du solltest einen AssetPackStateUpdatedListener implementieren, um den Installationsfortschritt von Asset-Packs im Blick zu behalten. Die Statusaktualisierungen sind nach Paket aufgeschlüsselt, damit der Status einzelner Asset-Packs verfolgt werden kann. Sie können verfügbare Asset-Pakete verwenden, bevor alle anderen Downloads für Ihre Anfrage abgeschlossen sind.

Kotlin

fun registerListener(listener: AssetPackStateUpdatedListener)
fun unregisterListener(listener: AssetPackStateUpdatedListener)

Java

void registerListener(AssetPackStateUpdatedListener listener)
void unregisterListener(AssetPackStateUpdatedListener listener)

Große Downloads

Wenn der Download größer als 200 MB ist und der Nutzer nicht mit einem WLAN verbunden ist, beginnt der Download erst, wenn der Nutzer ausdrücklich seine Einwilligung zum Fortsetzen des Downloads über eine mobile Datenverbindung erteilt. Wenn der Download groß ist und die WLAN-Verbindung des Nutzers unterbrochen wird, wird der Download pausiert und es ist eine ausdrückliche Einwilligung erforderlich, um mit einer mobilen Datenverbindung fortzufahren. Ein pausiertes Paket hat den Status WAITING_FOR_WIFI. Verwenden Sie die Methode showConfirmationDialog(), um den UI-Ablauf auszulösen, bei dem der Nutzer um seine Einwilligung gebeten wird.

Wenn die App diese Methode nicht aufruft, wird der Download pausiert und erst dann fortgesetzt, wenn der Nutzer wieder eine WLAN-Verbindung hat.

Erforderliche Nutzerbestätigung

Wenn ein Paket den Status REQUIRES_USER_CONFIRMATION hat, wird der Download erst fortgesetzt, wenn der Nutzer das Dialogfeld mit showConfirmationDialog() akzeptiert. Dieser Status kann auftreten, wenn die App von Google Play nicht erkannt wird, z. B. wenn sie per Sideload installiert wurde. Hinweis: Wenn Sie in diesem Fall showConfirmationDialog() aufrufen, wird die App aktualisiert. Nach dem Update müssen Sie die Assets noch einmal anfordern.

Im Folgenden finden Sie ein Beispiel für die Implementierung eines Listeners:

Kotlin

private val activityResultLauncher = registerForActivityResult(
    ActivityResultContracts.StartIntentSenderForResult()
) { result ->
    if (result.resultCode == RESULT_OK) {
        Log.d(TAG, "Confirmation dialog has been accepted.")
    } else if (result.resultCode == RESULT_CANCELED) {
        Log.d(TAG, "Confirmation dialog has been denied by the user.")
    }
}

assetPackManager.registerListener { assetPackState ->
  when(assetPackState.status()) {
    AssetPackStatus.PENDING -> {
      Log.i(TAG, "Pending")
    }
    AssetPackStatus.DOWNLOADING -> {
      val downloaded = assetPackState.bytesDownloaded()
      val totalSize = assetPackState.totalBytesToDownload()
      val percent = 100.0 * downloaded / totalSize

      Log.i(TAG, "PercentDone=" + String.format("%.2f", percent))
    }
    AssetPackStatus.TRANSFERRING -> {
      // 100% downloaded and assets are being transferred.
      // Notify user to wait until transfer is complete.
    }
    AssetPackStatus.COMPLETED -> {
      // Asset pack is ready to use. Start the game.
    }
    AssetPackStatus.FAILED -> {
      // Request failed. Notify user.
      Log.e(TAG, assetPackState.errorCode())
    }
    AssetPackStatus.CANCELED -> {
      // Request canceled. Notify user.
    }
    AssetPackStatus.WAITING_FOR_WIFI,
    AssetPackStatus.REQUIRES_USER_CONFIRMATION -> {
      if (!confirmationDialogShown) {
        assetPackManager.showConfirmationDialog(activityResultLauncher);
        confirmationDialogShown = true
      }
    }
    AssetPackStatus.NOT_INSTALLED -> {
      // Asset pack is not downloaded yet.
    }
    AssetPackStatus.UNKNOWN -> {
      Log.wtf(TAG, "Asset pack status unknown")
    }
  }
}

Java

assetPackStateUpdateListener = new AssetPackStateUpdateListener() {
    private final ActivityResultLauncher<IntentSenderRequest> activityResultLauncher =
      registerForActivityResult(
          new ActivityResultContracts.StartIntentSenderForResult(),
          new ActivityResultCallback<ActivityResult>() {
            @Override
            public void onActivityResult(ActivityResult result) {
              if (result.getResultCode() == RESULT_OK) {
                Log.d(TAG, "Confirmation dialog has been accepted.");
              } else if (result.getResultCode() == RESULT_CANCELED) {
                Log.d(TAG, "Confirmation dialog has been denied by the user.");
              }
            }
          });

    @Override
    public void onStateUpdate(AssetPackState assetPackState) {
      switch (assetPackState.status()) {
        case AssetPackStatus.PENDING:
          Log.i(TAG, "Pending");
          break;

        case AssetPackStatus.DOWNLOADING:
          long downloaded = assetPackState.bytesDownloaded();
          long totalSize = assetPackState.totalBytesToDownload();
          double percent = 100.0 * downloaded / totalSize;

          Log.i(TAG, "PercentDone=" + String.format("%.2f", percent));
          break;

        case AssetPackStatus.TRANSFERRING:
          // 100% downloaded and assets are being transferred.
          // Notify user to wait until transfer is complete.
          break;

        case AssetPackStatus.COMPLETED:
          // Asset pack is ready to use. Start the game.
          break;

        case AssetPackStatus.FAILED:
          // Request failed. Notify user.
          Log.e(TAG, assetPackState.errorCode());
          break;

        case AssetPackStatus.CANCELED:
          // Request canceled. Notify user.
          break;

        case AssetPackStatus.WAITING_FOR_WIFI:
        case AssetPackStatus.REQUIRES_USER_CONFIRMATION:
          if (!confirmationDialogShown) {
            assetPackManager.showConfirmationDialog(activityResultLauncher);
            confirmationDialogShown = true;
          }
          break;

        case AssetPackStatus.NOT_INSTALLED:
          // Asset pack is not downloaded yet.
          break;
        case AssetPackStatus.UNKNOWN:
          Log.wtf(TAG, "Asset pack status unknown")
          break;
      }
    }
}

Alternativ können Sie die Methode getPackStates() verwenden, um den Status aktueller Downloads abzurufen. AssetPackStates enthält den Downloadfortschritt, den Downloadstatus und alle Fehlercodes.

Auf Asset-Packs zugreifen

Du kannst über Dateisystemaufrufe auf ein Asset-Pack zugreifen, nachdem die Downloadanfrage den Status COMPLETED erreicht hat. Mit der Methode getPackLocation() können Sie den Stammordner des Asset-Pakets abrufen.

Assets werden im Verzeichnis assets im Stammverzeichnis des Asset-Pakets gespeichert. Sie können den Pfad zum assets-Verzeichnis mit der assetsPath()-Methode abrufen. So rufen Sie den Pfad zu einem bestimmten Asset ab:

Kotlin

private fun getAbsoluteAssetPath(assetPack: String, relativeAssetPath: String): String? {
    val assetPackPath: AssetPackLocation =
      assetPackManager.getPackLocation(assetPack)
      // asset pack is not ready
      ?: return null

    val assetsFolderPath = assetPackPath.assetsPath()
    // equivalent to: FilenameUtils.concat(assetPackPath.path(), "assets")
    return FilenameUtils.concat(assetsFolderPath, relativeAssetPath)
}

Java

private String getAbsoluteAssetPath(String assetPack, String relativeAssetPath) {
    AssetPackLocation assetPackPath = assetPackManager.getPackLocation(assetPack);

    if (assetPackPath == null) {
        // asset pack is not ready
        return null;
    }

    String assetsFolderPath = assetPackPath.assetsPath();
    // equivalent to: FilenameUtils.concat(assetPackPath.path(), "assets");
    String assetPath = FilenameUtils.concat(assetsFolderPath, relativeAssetPath);
    return assetPath;
}

Weitere Play Asset Delivery API-Methoden

Im Folgenden finden Sie einige weitere API-Methoden, die Sie in Ihrer App verwenden können.

Anfrage abbrechen

Mit cancel() können Sie einen aktiven Asset-Paket-Antrag abbrechen. Beachten Sie, dass diese Anfrage auf Best-Effort-Basis bearbeitet wird.

Asset-Paket entfernen

Verwende requestRemovePack() oder removePack(), um das Entfernen eines Asset-Pakets zu planen.

Speicherorte mehrerer Asset-Pakete abrufen

Mit getPackLocations() kannst du den Status mehrerer Asset-Packs gleichzeitig abfragen. Es wird dann eine Karte mit den Asset-Packs und ihren Speicherorten zurückgegeben. Die von getPackLocations() zurückgegebene Karte enthält einen Eintrag für jedes Paket, das derzeit heruntergeladen und auf dem neuesten Stand ist.

Nächster Schritt

Testen Sie die Play Asset Delivery lokal und bei Google Play.