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 kodu Java.

Tworzenie aplikacji w językach Kotlin i Java

Aby wbudować Play Asset Delivery w pakiet Android App Bundle projektu, wykonaj te czynności. Nie musisz używać Android Studio, aby wykonać te 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 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 do niego ten kod. Pamiętaj, aby podać nazwę pakietu zasobów i tylko jeden typ dostarczania:

    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 podkatalog src/main/assets.

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

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

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

  9. (Opcjonalnie) Dołącz bibliotekę Play Asset Delivery, jeśli planujesz używać dostarczania po pobraniu i na żądanie.

    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, która umożliwia wysyłanie żądań dotyczących pakietów zasobów, zarządzanie pobieraniem i uzyskiwanie dostępu do zasobów. Najpierw dodaj do projektu bibliotekę Play Asset Delivery.

Interfejs API implementuje się zgodnie z typem dostarczania pakietu zasobów, do którego chcesz uzyskać dostęp. Te kroki są przedstawione na schemacie blokowym poniżej.

Schemat procesu pakietu zasobów w języku programowania Java

Rysunek 1. Diagram przepływu dotyczący uzyskiwania dostępu do pakietów zasobów

Dostarczanie w czasie 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 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");

Szybka dostawa i dostawa na żądanie

W sekcjach poniżej dowiesz się, jak uzyskać informacje o pakietach zasobów przed ich pobraniem, jak wywołać interfejs API, aby rozpocząć pobieranie, 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 te 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 zasobów lub zasoby są niedostępne

Uzyskiwanie informacji o pobieraniu pakietów zasobów

Aplikacje muszą ujawniać rozmiar pliku do pobrania przed pobraniem pakietu zasobów. Użyj metody requestPackStates() lub getPackStates(), aby określić rozmiar pobieranego pakietu i sprawdzić, czy jest on już pobierany.

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, który jest kluczowany 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 AssetPackState podają rozmiar pakietu zasobów, ilość pobranych danych (jeśli jest to wymagane) i ilość danych już przesłanych do aplikacji:

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

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

Zainstaluj

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

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, o który poproszono za pomocą requestFetch() lub fetch(), jest już pobierany, zwracany jest stan pobierania i nie rozpoczyna się dodatkowe pobieranie.

Monitorowanie stanów pobierania

Aby śledzić postępy instalacji pakietów zasobów, zaimplementuj AssetPackStateUpdatedListener. Aktualizacje stanu są podzielone według pakietów, aby ułatwić śledzenie stanu poszczególnych pakietów zasobów. Możesz zacząć korzystać z dostępnych pakietów zasobów, zanim zostaną pobrane wszystkie pozostałe pliki, o które prosisz.

Kotlin

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

Java

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

pobierania dużych plików,

Jeśli rozmiar pobieranych danych przekracza 200 MB, a użytkownik nie jest połączony z Wi-Fi, pobieranie nie rozpocznie się, dopóki użytkownik nie wyrazi wyraźnej zgody na pobieranie przy użyciu komórkowego połączenia transmisji danych. Podobnie, jeśli pobierany plik jest duży, a użytkownik straci połączenie z Wi-Fi, pobieranie zostanie wstrzymane i będzie wymagało wyraźnej zgody na kontynuowanie przy użyciu komórkowego połączenia danych. Wstrzymany pakiet ma stan WAITING_FOR_WIFI. Aby uruchomić proces interfejsu, który poprosi użytkownika o zgodę, użyj metody showConfirmationDialog().

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

Wymagana zgoda użytkownika

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

Oto przykładowa implementacja 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;
      }
    }
}

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

Dostęp do pakietu zasobów możesz uzyskać za pomocą wywołań systemu plików, gdy prośba o pobranie osiągnie stan COMPLETED. Aby uzyskać folder główny pakietu zasobów, użyj metody getPackLocation().

Komponenty są przechowywane w katalogu assets w katalogu głównym pakietu zasobów. Ścieżkę do katalogu assets możesz uzyskać za pomocą metody assetsPath(). Aby uzyskać ścieżkę do konkretnego komponentu, 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ę

Użyj cancel(), aby anulować aktywne żądanie pakietu zasobów. Pamiętaj, że ta prośba jest operacją wykonywaną w miarę możliwości.

Usuwanie pakietu zasobów

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

Pobieranie lokalizacji wielu pakietów zasobów

Użyj metody getPackLocations(), aby zbiorczo sprawdzić stan wielu pakietów zasobów. Zwróci ona mapę pakietów zasobów i ich lokalizacji. Mapa zwracana przez funkcję getPackLocations() zawiera wpis dotyczący każdego pakietu, który jest obecnie pobrany i aktualny.

Następny krok

Przetestuj Play Asset Delivery lokalnie i w Google Play.