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

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

Tworzenie wersji na Kotlin i Java

Aby dodać do pakietu aplikacji na Androida w projekcie dostawę zasobów w Google Play, wykonaj te czynności. Aby wykonać te czynności, nie musisz używać Android Studio.

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

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

  3. W katalogu pakietu zasobów utwórz plik build.gradle i dodaj do niego ten kod: Pamiętaj, aby podać nazwę pakietu zasobów i tylko jeden typ przesyłania:

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

    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. W pliku settings.gradle projektu umieść wszystkie pakiety komponentów, jak pokazano poniżej:

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

  7. Umieść zasoby w katalogu src/main/assets. Możesz tu też tworzyć podkatalogi. Struktura katalogu 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. Wygeneruj pakiet Android App Bundle za pomocą Gradle. W wygenerowanym pakiecie aplikacji katalog na poziomie katalogu głównego zawiera teraz:

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

    Gradle generuje plik manifestu dla każdego pakietu komponentów i wyprowadza domyślnie assets/ Katalog.

  9. (Opcjonalnie) Jeśli planujesz korzystać z szybkiej dostawy i dostawy na żądanie, dodaj Bibliotekę dostawy zasobów Play.

    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. (Opcjonalnie) Skonfiguruj pakiet aplikacji tak, aby obsługiwał różne formaty kompresji tekstur.

Integracja z interfejsem Play Asset Delivery API

Interfejs Play Asset Delivery API w języku Java udostępnia klasę AssetPackManager, która służy do żądania pakietów zasobów, zarządzania pobieraniem i dostępu do zasobów. Najpierw dodaj do projektu bibliotekę Play Asset Delivery.

Interfejs API jest implementowany zgodnie z typem dostawy pakietu komponentów, do którego chcesz uzyskać dostęp. Te kroki są pokazane na poniższym schemacie.

Schemat blokowy pakietu komponentów dla języka programowania Java

Rysunek 1. Schemat procesu uzyskiwania dostępu do pakietów komponentów

Przesyłanie w momencie instalacji

Pakiety komponentó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 AssetManager API w języku Java:

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 dostawy na żądanie

W następnych sekcjach dowiesz się, jak uzyskać informacje o pakietach komponentów przed ich pobraniem, jak wywołać interfejs API, aby rozpocząć pobieranie, oraz jak uzyskać dostęp do pobranych pakietów. Te sekcje dotyczą pakietów komponentów fast-followon-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 obiekt AssetPackLocation Folder główny pakietu zasobów jest gotowy do natychmiastowego użycia pod adresem assetsPath().
null Nieznany pakiet komponentów lub komponenty są niedostępne

Pobieranie informacji o pakietach zasobów

Aplikacje muszą podawać rozmiar pliku do pobrania przed pobraniem pakietu zasobów. Aby określić rozmiar pobierania 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 zwracająca obiekt AssetPackStates, a getPackStates() to metoda asynchroniczna zwracająca Task<AssetPackStates>. Metoda packStates() obiektu AssetPackStates zwraca obiekt Map<String, AssetPackState>. Ta mapa zawiera stan każdego żądanego pakietu zasobów, posortowanego według 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 AssetPackStatepozwalają określić rozmiar pakietu zasobów, pobraną do tej pory ilość danych (jeśli została podana) i ilość danych już przeniesioną do aplikacji:

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

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

Zainstaluj

Aby pobrać pakiet komponentów po raz pierwszy lub wywołać jego aktualizację, 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 AssetPackStates zawierający listę pakietów oraz ich początkowe stany i rozmiary pobierania. Jeśli pakiet zasobów zażądany za pomocą interfejsu requestFetch() lub fetch() jest już pobierany, zwracany jest stan pobierania i nie rozpoczyna się dodatkowe pobieranie.

Monitorowanie stanów pobierania

Aby śledzić postęp instalacji pakietów komponentów, należy zaimplementować AssetPackStateUpdatedListener. Aktualizacje stanu są podzielone według pakietu, aby umożliwić śledzenie stanu poszczególnych pakietów komponentów. Możesz zacząć korzystać z dostępnych pakietów komponentów, zanim wszystkie inne pliki zostaną pobrane.

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 pobieranie jest większe niż 200 MB, a użytkownik nie korzysta z Wi-Fi, pobieranie nie rozpocznie się, dopóki użytkownik nie wyrazi wyraźnej zgody na kontynuowanie pobierania za pomocą połączenia z komórkową transmisją danych. Podobnie, jeśli pobieranie jest duże, a użytkownik straci połączenie z Wi-Fi, pobieranie zostanie wstrzymane i do dalszego pobierania za pomocą mobilnej transmisji danych będzie wymagana wyraźna zgoda. Wstrzymany pakiet ma stanWAITING_FOR_WIFI. Aby wywołać proces w interfejsie, który poprosi użytkownika 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 będzie ponownie połączony z siecią Wi-Fi.

Wymagane potwierdzenie użytkownika

Jeśli pakiet ma stan REQUIRES_USER_CONFIRMATION, pobieranie nie rozpocznie się, dopóki użytkownik nie zaakceptuje wyświetlonego okna dialogowego showConfirmationDialog(). Ten stan może wystąpić, gdy aplikacja nie jest rozpoznawana przez Google Play – na przykład jeśli została zainstalowana z innego urządzenia. Pamiętaj, że w tym przypadku wywołanie funkcji showConfirmationDialog() spowoduje zaktualizowanie aplikacji. Po aktualizacji musisz ponownie poprosić o zasoby.

Poniżej znajduje się przykładowa implementacja odbiornika:

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 sprawdzić stan bieżących pobrań. AssetPackStates zawiera postęp pobierania, stan pobierania i kody błędów.

Dostęp do pakietów zasobów

Po tym, jak żądanie pobierania dotrze do stanu COMPLETED, możesz uzyskać dostęp do pakietu zasobów za pomocą wywołań systemu plików. Użyj metody getPackLocation(), aby pobrać folder główny pakietu komponentów.

Komponenty są przechowywane w katalogu assets w katalogu głównym pakietu komponentów. Ścieżkę do katalogu assets możesz uzyskać, używając metody assetsPath(). Aby uzyskać ścieżkę do konkretnego komponentu:

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żywać w aplikacji.

Anuluj prośbę

Aby anulować prośbę o aktywne pakiety komponentów, kliknij cancel(). Pamiętaj, że ta prośba jest realizowana zgodnie z zasadą „dokładamy wszelkich starań”.

Usuwanie pakietu komponentów

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

Pobieranie lokalizacji wielu pakietów komponentów

Użyj parametru getPackLocations(), aby zapytać o stan wielu pakietów zasobów zbiorczo. Zwróci on mapę pakietów zasobów i ich lokalizacji. Mapa zwracana przez funkcję getPackLocations()zawiera wpis dla każdego pakietu, który jest obecnie pobrany i aktualny.

Następny krok

Testuj Play Asset Delivery lokalnie i z Google Play.