Zintegruj przesyłanie zasobów (kotlin i Java)

Aby uzyskać dostęp do pakietów zasobów aplikacji z kodu Java, wykonaj czynności opisane w tym przewodniku.

Tworzenie aplikacji w Kotlinie i Javie

Aby wdrożyć Play Asset Delivery w pakiecie Android App Bundle projektu, wykonaj te czynności. Nie musisz używać Android Studio.

  1. Zaktualizuj wersję wtyczki Androida do obsługi Gradle w pliku build.gradle projektu do wersji 4.0.0 lub nowszej.

  2. W katalogu najwyższego poziomu projektu utwórz katalog pakietu zasobów. Nazwa tego katalogu jest używana jako nazwa pakietu zasobów. Nazwy pakietów zasobów muszą zaczynać się od litery i mogą zawierać tylko litery, cyfry i podkreślenia.

  3. W katalogu pakietu zasobów utwórz plik build.gradle i dodaj ten kod. Pamiętaj, aby określić nazwę pakietu zasobów i tylko 1 typ dostawy:

    Dynamiczny

    // 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. W pliku build.gradle aplikacji projektu dodaj nazwę każdego pakietu zasobów w projekcie, jak pokazano poniżej:

    Dynamiczny

    // 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. W pliku settings.gradle projektu uwzględnij wszystkie pakiety zasobów w projekcie, jak pokazano poniżej:

    Dynamiczny

    // 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. W katalogu pakietu zasobów utwórz ten podkatalog: src/main/assets.

  7. Umieść zasoby w katalogu src/main/assets. Możesz też utworzyć w nim podkatalogi. Struktura katalogów aplikacji powinna teraz wyglądać tak:

    • build.gradle
    • settings.gradle
    • app/
    • asset-pack-name/build.gradle
    • asset-pack-name/src/main/assets/your-asset-directories
  8. Utwórz pakiet Android App Bundle za pomocą Gradle. W wygenerowanym pakiecie aplikacji katalog główny zawiera teraz te elementy:

    • asset-pack-name/manifest/AndroidManifest.xml: konfiguruje identyfikator pakietu zasobów i tryb wyświetlania.
    • asset-pack-name/assets/your-asset-directories: katalog zawierający wszystkie zasoby dostarczane w ramach pakietu zasobów.

    Gradle generuje manifest dla każdego pakietu zasobów i tworzy katalog assets/.

  9. (Opcjonalnie) Jeśli planujesz korzystać z dostawy typu fast-follow i na żądanie, uwzględnij bibliotekę Play Asset Delivery.

    Dynamiczny

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

    Kotlin

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

  10. (Opcjonalnie) Skonfiguruj pakiet aplikacji tak, aby obsługiwał różne formaty kompresji tekstur.

Integracja z interfejsem Play Asset Delivery API

Interfejs Play Asset Delivery Java API udostępnia klasę AssetPackManager do wysyłania próśb o pakiety zasobów, zarządzania pobieraniem i uzyskiwania dostępu do zasobów. Najpierw dodaj bibliotekę Play Asset Delivery do projektu.

Ten interfejs API implementujesz zgodnie z typem dostawy pakietu zasobów, do którego chcesz uzyskać dostęp. Te czynności są przedstawione na tym schemacie blokowym.

Schemat pakietu zasobów w języku programowania Java

Rysunek 1. Schemat blokowy uzyskiwania dostępu do pakietów zasobów

Dostawa typu install-time

Pakiety zasobów skonfigurowane jako install-time są dostępne od razu po uruchomieniu aplikacji. Aby uzyskać dostęp do zasobów udostępnianych w tym trybie, użyj interfejsu Java AssetManager API:

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");

Dostawa typu fast-follow i na żądanie

W kolejnych sekcjach dowiesz się, jak uzyskać informacje o pakietach zasobów przed ich pobraniem, jak wywołać interfejs API, aby rozpocząć pobieranie, a następnie jak uzyskać dostęp do pobranych pakietów. Te sekcje dotyczą pakietów zasobów fast-follow i on-demand.

Sprawdź stan

Każdy pakiet zasobów jest przechowywany w osobnym folderze w pamięci wewnętrznej aplikacji. Aby określić folder główny pakietu zasobów, użyj metody getPackLocation(). Ta metoda zwraca te wartości:

Zwracana wartość Stan
Prawidłowy AssetPackLocation obiekt Folder główny pakietu zasobów jest gotowy do natychmiastowego dostępu w assetsPath()
null Nieznany pakiet zasobów lub zasoby są niedostępne

Pobieranie informacji o pakietach zasobów

Przed pobraniem pakietu zasobów aplikacje muszą ujawnić rozmiar pobieranego pliku. Aby określić rozmiar pobieranego pliku i sprawdzić, czy pakiet jest już pobierany, użyj metody requestPackStates() lub getPackStates().

Kotlin

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

Java

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

requestPackStates() to funkcja zawieszająca, która zwraca obiekt AssetPackStates , a getPackStates() to metoda asynchroniczna, która zwraca Task<AssetPackStates>. Metoda packStates() obiektu AssetPackStates zwraca Map<String, AssetPackState>. Ta mapa zawiera stan każdego żądanego pakietu zasobów, z kluczem w postaci jego nazwy:

Kotlin

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

Java

Map<String, AssetPackState> AssetPackStates#packStates()

Ostatnie żądanie jest pokazane poniżej:

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;
            })

Te metody podają rozmiar pakietu zasobów, dotychczas pobraną ilość (jeśli została o to poproszona) oraz ilość już przesłaną do aplikacji:AssetPackState

Aby uzyskać stan pakietu zasobów, użyj status() metody, która zwraca stan jako liczbę całkowitą odpowiadającą stałemu polu w AssetPackStatus klasie. Pakiet zasobów, który nie jest jeszcze zainstalowany, ma stan AssetPackStatus.NOT_INSTALLED.

Jeśli żądanie się nie powiedzie, użyj metody errorCode(), której wartość zwracana odpowiada stałemu polu w klasie AssetPackErrorCode.

Zainstaluj

Aby pobrać pakiet zasobów po raz pierwszy lub wywołać aktualizację pakietu zasobów, użyj metody requestFetch() lub fetch():

Kotlin

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

Java

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

Ta metoda zwraca obiekt zawierający listę pakietów oraz ich początkowe stany i rozmiary pobierania.AssetPackStates Jeśli pakiet zasobów, o który poproszono za pomocą requestFetch() lub fetch(), jest już pobierany, zwracany jest stan pobierania i nie jest rozpoczynane dodatkowe pobieranie.

Monitorowanie stanów pobierania

Aby śledzić postęp instalacji pakietów zasobów, zaimplementuj AssetPackStateUpdatedListener. Aktualizacje stanu są dzielone na pakiety, aby można było śledzić stan poszczególnych pakietów zasobów. Możesz zacząć korzystać z dostępnych pakietów zasobów, zanim zakończą się wszystkie inne pobierania w ramach żądania.

Kotlin

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

Java

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

Pobieranie dużych plików

Jeśli pobierany plik ma więcej niż 200 MB i użytkownik nie korzysta z Wi-Fi, pobieranie nie rozpocznie się, dopóki użytkownik nie wyrazi wyraźnie zgody na kontynuowanie pobierania za pomocą połączenia komórkowego. Podobnie jeśli pobierany plik jest duży, a użytkownik utraci połączenie z Wi-Fi, pobieranie zostanie wstrzymane i wymagana będzie wyraźna zgoda na kontynuowanie pobierania za pomocą połączenia komórkowego. Wstrzymany pakiet ma stan WAITING_FOR_WIFI. Aby wywołać interfejs użytkownika z prośbą o zgodę, użyj metody showConfirmationDialog().

Jeśli aplikacja nie wywoła tej metody, pobieranie zostanie wstrzymane i wznowione automatycznie dopiero wtedy, gdy użytkownik ponownie połączy się z Wi-Fi.

Wymagane potwierdzenie użytkownika

Jeśli pakiet ma stan REQUIRES_USER_CONFIRMATION, pobieranie nie będzie kontynuowane, dopóki użytkownik nie zaakceptuje okna dialogowego wyświetlanego za pomocą showConfirmationDialog(). Ten stan może wystąpić, gdy aplikacja nie jest rozpoznawana przez Sklep Play, np. jeśli została zainstalowana z innego urządzenia. Wywołanie showConfirmationDialog() w tym przypadku spowoduje zaktualizowanie aplikacji. Po aktualizacji musisz ponownie poprosić o zasoby.

Oto przykładowa implementacja słuchacza:

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;
      }
    }
}

Możesz też użyć metody getPackStates(), aby uzyskać stan bieżących pobrań. AssetPackStates zawiera postęp pobierania, stan pobierania i kody błędów.

Uzyskiwanie dostępu do pakietów zasobów

Po osiągnięciu przez żądanie pobierania stanu COMPLETED możesz uzyskać dostęp do pakietu zasobów za pomocą wywołań systemu plików. Aby uzyskać folder główny pakietu zasobów, użyj metody getPackLocation().

Zasoby są przechowywane w katalogu assets w katalogu głównym pakietu zasobów. Ścieżkę do katalogu assets możesz uzyskać za pomocą metody pomocniczej assetsPath(). Aby uzyskać ścieżkę do konkretnego zasobu, użyj tej metody:

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;
}

Inne metody interfejsu Play Asset Delivery API

Oto kilka dodatkowych metod interfejsu API, których możesz użyć w aplikacji.

Anuluj prośbę

Aby anulować aktywne żądanie pakietu zasobów, użyj cancel(). Pamiętaj, że to żądanie jest operacją, która ma na celu podjęcie wszelkich możliwych działań.

Usuwanie pakietu zasobów

Aby zaplanować usunięcie pakietu zasobów, użyj requestRemovePack() lub removePack().

Pobieranie lokalizacji wielu pakietów zasobów

Aby zbiorczo sprawdzić stan wielu pakietów zasobów, użyj getPackLocations() , która zwraca mapę pakietów zasobów i ich lokalizacji. Mapa zwracana przez getPackLocations() zawiera wpis dla każdego pakietu, który jest obecnie pobrany i aktualny.

Następny krok

Testowanie Play Asset Delivery lokalnie i w Google Play.