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

Wykonaj czynności opisane w tym przewodniku, aby uzyskać dostęp do pakietów zasobów aplikacji z poziomu kodu w Javie.

Tworzenie kompilacji dla Kotlin i Javy

Wykonaj poniższe czynności, aby dodać funkcję Play Asset Delivery do pakietu Android App Bundle swojego projektu. Nie musisz używać Android Studio do wykonania tych czynności.

  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 dla pakietu zasobów. Ta nazwa 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 oraz podkreślenia.

  3. W katalogu pakietu zasobów utwórz plik build.gradle i dodaj ten kod. Podaj nazwę pakietu zasobów i tylko jeden typ przesyłania:

    Odlotowy

    // 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 w projekcie dodaj nazwę każdego pakietu zasobów w projekcie zgodnie z poniższym przykładem:

    Odlotowy

    // 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 umieść wszystkie pakiety zasobów w podany niżej sposób:

    Odlotowy

    // 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. Tutaj możesz też tworzyć 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:

    • asset-pack-name/manifest/AndroidManifest.xml: konfiguruje identyfikator pakietu zasobów i tryb jego przesyłania.
    • asset-pack-name/assets/your-asset-directories: katalog zawierający wszystkie zasoby dostarczone w ramach pakietu zasobów

    Gradle generuje plik manifestu dla każdego pakietu zasobów i wyświetla katalog assets/.

  9. (Opcjonalnie) Dołącz Bibliotekę Play Asset Delivery, jeśli zamierzasz korzystać z szybkiej dostawy i na żądanie.

    Odlotowy

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

    Kotlin

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

  10. (Opcjonalnie) Skonfiguruj pakiet aplikacji pod kątem obsługi różnych formatów kompresji tekstur.

Integracja z interfejsem Play Asset Delivery API

Interfejs Play Asset Delivery Java API udostępnia klasę AssetPackManager służącą do wysyłania żądań pakietów zasobów, zarządzania pobieraniem i uzyskiwania dostępu do zasobów. Pamiętaj, aby najpierw dodać do projektu bibliotekę Play Asset Delivery.

Ten interfejs API implementujesz zgodnie z typem przesyłania pakietu zasobów, do którego chcesz uzyskać dostęp. Te kroki przedstawiono na poniższym schemacie blokowym.

Diagram przepływu pakietu zasobów dla języka programowania Java

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

Dostawa podczas instalacji

Pakiety zasobów skonfigurowane jako install-time są dostępne od razu po uruchomieniu aplikacji. Aby uzyskać dostęp do zasobów wyświetlanych 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");

Szybkie śledzenie i na żądanie

Z sekcji poniżej dowiesz się, jak uzyskać informacje o pakietach zasobów przed ich pobraniem, jak wywołać interfejs API w celu rozpoczęcia pobierania i 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 następujące wartości:

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

Pobieranie informacji o pakietach zasobów do pobrania

Przed pobraniem pakietu zasobów aplikacje muszą ujawniać 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 zawieszania, która zwraca obiekt AssetPackStates, natomiast getPackStates() to metoda asynchroniczna, która zwraca obiekt Task<AssetPackStates>. Metoda packStates() obiektu AssetPackStates zwraca wartość Map<String, AssetPackState>. Ta mapa zawiera stan każdego żądanego pakietu zasobów, kluczowany według jego nazwy:

Kotlin

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

Java

Map<String, AssetPackState> AssetPackStates#packStates()

Ostateczne żądanie wygląda tak:

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 AssetPackState podają rozmiar pakietu zasobów, ilość pobranej do tej pory kwoty (w razie potrzeby) oraz kwotę już przekazaną do aplikacji:

Aby uzyskać stan pakietu zasobów, użyj metody status(), która zwraca stan w postaci liczby całkowitej odpowiadającej stałemu polu w klasie AssetPackStatus. Pakiet zasobów, który nie został jeszcze zainstalowany, ma stan AssetPackStatus.NOT_INSTALLED.

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

Zainstaluj

Aby po raz pierwszy pobrać pakiet zasobów, użyj metody requestFetch() lub fetch() lub wywołaj aktualizację pakietu zasobów, aby ukończyć ten proces:

Kotlin

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

Java

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

Ta metoda zwraca obiekt AssetPackStates zawierający listę pakietów oraz ich początkowych stanów i rozmiarów pobierania. Jeśli żądanie pakietu zasobów z programu requestFetch() lub fetch() jest już pobierane, zostanie zwrócony stan pobierania i nie rozpocznie się żadne dodatkowe pobieranie.

Monitorowanie stanów pobierania

Aby śledzić postęp instalacji pakietów zasobów, musisz wdrożyć AssetPackStateUpdatedListener. Aktualizacje stanu są podzielone na pakiety, aby umożliwić śledzenie stanu poszczególnych pakietów zasobów. Możesz zacząć z nich korzystać, zanim wszystkie inne operacje pobierania związane z Twoim żądaniem zostaną pobrane.

Kotlin

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

Java

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

Duże pliki do pobrania

Jeśli rozmiar pobieranego pliku przekracza 200 MB, a użytkownik nie korzysta z Wi-Fi, pobieranie rozpocznie się dopiero wtedy, gdy użytkownik wyraźnie wyrazi zgodę na pobieranie przy użyciu komórkowego połączenia danych. Podobnie jeśli pobierany plik jest duży i użytkownik utraci połączenie z Wi-Fi, pobieranie zostaje wstrzymane, a kontynuowanie korzystania z mobilnego połączenia danych wymaga wyraźnej zgody użytkownika. Wstrzymany pakiet ma stan WAITING_FOR_WIFI. Aby uruchomić proces interfejsu i poprosić użytkownika o zgodę na wykorzystanie danych, użyj metody showConfirmationDialog().

Pamiętaj, że jeśli aplikacja nie wywoła tej metody, pobieranie zostanie wstrzymane i wznowione automatycznie tylko wtedy, gdy użytkownik odzyska połączenie z Wi-Fi.

Wymagane potwierdzenie użytkownika

Jeśli pakiet ma stan REQUIRES_USER_CONFIRMATION, pobieranie nie zostanie ukończone, dopóki użytkownik nie zaakceptuje okna wyświetlanego przy użyciu showConfirmationDialog(). Ten stan może wystąpić, gdy aplikacja nie została rozpoznana przez Google Play – na przykład jeśli została zainstalowana z innego urządzenia. Pamiętaj, że wywołanie metody showConfirmationDialog() spowoduje zaktualizowanie aplikacji. Po aktualizacji musisz ponownie poprosić o zasoby.

Oto przykład implementacji detektora:

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

Aby sprawdzić stan pobranych plików, możesz też użyć metody getPackStates(). AssetPackStates zawiera informacje o postępie pobierania, stanie pobierania i kodach błędów dotyczących niepowodzenia.

Dostęp do pakietów zasobów

Dostęp do pakietu zasobów możesz uzyskać za pomocą wywołań systemu plików, gdy żądanie pobierania osiągnie stan COMPLETED. 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żka do katalogu assets możesz uzyskać, korzystając z wygodnej metody assetsPath(). Aby uzyskać ścieżkę do określonego 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 Play Asset Delivery API

Poniżej znajdziesz kilka dodatkowych metod interfejsu API, których możesz używać w swojej aplikacji.

Anuluj prośbę

Użyj operatora cancel(), aby anulować żądanie aktywnego pakietu zasobów. Pamiętaj, że jest to działanie z możliwością najlepszej obsługi.

Usuwanie pakietu zasobów

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

Pobieranie lokalizacji wielu pakietów zasobów

Aby zbiorczo wysłać zapytanie o stan wielu pakietów zasobów, użyj narzędzia getPackLocations(), aby wyświetlić mapę pakietów zasobów i ich lokalizacje. Mapa zwrócona przez funkcję getPackLocations() zawiera wpis każdego pakietu, który jest obecnie pobrany i aktualny.

Następny krok

Przetestuj przesyłanie zasobów Play lokalnie i z Google Play.