Интеграция доставки активов (Kotlin и Java), Интеграция доставки активов (Kotlin и Java)

Используйте шаги, описанные в этом руководстве, чтобы получить доступ к пакетам ресурсов вашего приложения из кода Java.

Сборка для Kotlin и Java

Выполните следующие действия, чтобы встроить Play Asset Delivery в пакет Android App Bundle вашего проекта. Вам не нужно использовать Android Studio для выполнения этих действий.

  1. Обновите версию плагина Android Gradle в файле build.gradle вашего проекта до 4.0.0 или более поздней версии.

  2. В каталоге верхнего уровня вашего проекта создайте каталог для пакета ресурсов. Это имя каталога используется как имя пакета ресурсов. Имена пакетов ресурсов должны начинаться с буквы и могут содержать только буквы, цифры и символы подчеркивания.

  3. В каталоге пакета ресурсов создайте файл build.gradle и добавьте следующий код. Обязательно укажите название пакета активов и только один тип доставки:

    классный

    // 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 ]"
        }
    }
    

    Котлин

    // 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. В файле приложения build.gradle проекта добавьте имя каждого пакета ресурсов в вашем проекте, как показано ниже:

    классный

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

    Котлин

    // In the app build.gradle.kts file:
    android {
        ...
        assetPacks += listOf(":asset-pack-name", ":asset-pack2-name")
    }
    
  5. В файле settings.gradle проекта включите все пакеты ресурсов вашего проекта, как показано ниже:

    классный

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

    Котлин

    // In the settings.gradle.kts file:
    include(":app")
    include(":asset-pack-name")
    include(":asset-pack2-name")
    
  6. В каталоге пакета ресурсов создайте следующий подкаталог: src/main/assets .

  7. Поместите ресурсы в каталог src/main/assets . Здесь вы также можете создавать подкаталоги. Структура каталогов вашего приложения теперь должна выглядеть следующим образом:

    • build.gradle
    • settings.gradle
    • app/
    • asset-pack-name /build.gradle
    • asset-pack-name /src/main/assets/ your-asset-directories
  8. Создайте пакет приложений Android с помощью Gradle . В созданном пакете приложений каталог корневого уровня теперь включает следующее:

    • asset-pack-name /manifest/AndroidManifest.xml : настраивает идентификатор и режим доставки пакета ресурсов.
    • asset-pack-name /assets/ your-asset-directories : каталог, содержащий все активы, поставляемые как часть пакета ресурсов.

    Gradle генерирует манифест для каждого пакета ресурсов и выводит для вас каталог assets/ .

  9. (Необязательно) Включите библиотеку доставки ресурсов Play , если вы планируете использовать быструю доставку и доставку по требованию.

    классный

    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"
    

    Котлин

    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. (Необязательно) Настройте пакет приложений для поддержки различных форматов сжатия текстур .

Интеграция с API доставки ресурсов Play.

API-интерфейс Play Asset Delivery Java предоставляет класс AssetPackManager для запроса пакетов ресурсов, управления загрузками и доступа к ресурсам. Обязательно сначала добавьте в свой проект библиотеку доставки ресурсов Play .

Вы реализуете этот API в соответствии с типом доставки пакета ресурсов, к которому вы хотите получить доступ. Эти шаги показаны на следующей блок-схеме.

Блок-схема пакета ресурсов для языка программирования Java

Рисунок 1. Блок-схема доступа к пакетам ресурсов

Доставка во время установки

Пакеты ресурсов, настроенные для установки во install-time сразу же доступны при запуске приложения. Используйте API Java AssetManager для доступа к ресурсам, обслуживаемым в этом режиме:

Котлин

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

Ява

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

Быстрая доставка и доставка по требованию

В следующих разделах показано, как получить информацию о пакетах ресурсов перед их загрузкой, как вызвать API, чтобы начать загрузку, а затем как получить доступ к загруженным пакетам. Эти разделы относятся к пакетам ресурсов fast-follow и on-demand .

Проверить статус

Каждый пакет ресурсов хранится в отдельной папке во внутренней памяти приложения. Используйте метод getPackLocation() , чтобы определить корневую папку пакета ресурсов. Этот метод возвращает следующие значения:

Возвращаемое значение Статус
Действительный объект AssetPackLocation . Корневая папка пакета ресурсов готова к немедленному доступу с помощью assetsPath()
null Неизвестный пакет ресурсов или активы недоступны.

Получить информацию о загрузке пакетов ресурсов

Приложения должны раскрывать размер загрузки перед получением пакета ресурсов. Используйте метод requestPackStates() или getPackStates() чтобы определить размер загрузки и узнать, загружается ли пакет уже.

Котлин

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

Ява

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

requestPackStates() — это функция приостановки, возвращающая объект AssetPackStates , а getPackStates() — асинхронный метод, который возвращает Task<AssetPackStates> . Метод packStates() объекта AssetPackStates возвращает Map<String, AssetPackState> . Эта карта содержит состояние каждого запрошенного пакета активов с указанием его имени:

Котлин

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

Ява

Map<String, AssetPackState> AssetPackStates#packStates()

Окончательный запрос представлен следующим образом:

Котлин

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

Ява

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

Следующие методы AssetPackState предоставляют размер пакета ресурсов, загруженную сумму (если запрошено) и сумму, уже переданную в приложение:

Чтобы получить статус пакета ресурсов, используйте метод status() , который возвращает статус в виде целого числа, соответствующего константному полю в классе AssetPackStatus . Пакет ресурсов, который еще не установлен, имеет статус AssetPackStatus.NOT_INSTALLED .

Если запрос не выполнен, используйте метод errorCode() , возвращаемое значение которого соответствует константному полю в классе AssetPackErrorCode .

Установить

Используйте метод requestFetch() или fetch() для первой загрузки пакета ресурсов или вызовите обновление пакета ресурсов для завершения:

Котлин

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

Ява

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

Этот метод возвращает объект AssetPackStates , содержащий список пакетов, а также их начальные состояния загрузки и размеры. Если пакет ресурсов, запрошенный через requestFetch() или fetch() , уже загружается, возвращается статус загрузки и дополнительная загрузка не начинается.

Мониторинг состояний загрузки

Вам следует реализовать AssetPackStateUpdatedListener для отслеживания хода установки пакетов ресурсов. Обновления статуса разбиваются по пакетам, чтобы можно было отслеживать статус отдельных пакетов активов. Вы можете начать использовать доступные пакеты ресурсов до того, как будут завершены все остальные загрузки по вашему запросу.

Котлин

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

Ява

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

Большие загрузки

Если размер загрузки превышает 200 МБ и пользователь не подключен к Wi-Fi, загрузка не начнется до тех пор, пока пользователь явно не даст свое согласие на продолжение загрузки с использованием мобильного подключения для передачи данных. Аналогичным образом, если загрузка большая и пользователь теряет Wi-Fi, загрузка приостанавливается и требуется явное согласие для продолжения использования мобильного подключения для передачи данных. Приостановленный пакет имеет состояние WAITING_FOR_WIFI . Чтобы запустить поток пользовательского интерфейса для запроса согласия пользователя, используйте метод showConfirmationDialog() .

Обратите внимание: если приложение не вызывает этот метод, загрузка приостанавливается и возобновляется автоматически только тогда, когда пользователь снова подключается к Wi-Fi.

Требуется подтверждение пользователя

Если пакет имеет статус REQUIRES_USER_CONFIRMATION , загрузка не продолжится, пока пользователь не примет диалоговое окно, отображаемое с помощью showConfirmationDialog() . Этот статус может возникнуть, если приложение не распознается Play, например, если приложение было загружено неопубликовано. Обратите внимание, что вызов showConfirmationDialog() в этом случае приведет к обновлению приложения. После обновления вам нужно будет снова запросить активы.

Ниже приведен пример реализации прослушивателя:

Котлин

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

Ява

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

Альтернативно вы можете использовать метод getPackStates() , чтобы получить статус текущих загрузок. AssetPackStates содержит ход загрузки, состояние загрузки и любые коды ошибок сбоя.

Доступ к пакетам ресурсов

Вы можете получить доступ к пакету ресурсов с помощью вызовов файловой системы после того, как запрос на загрузку достигнет состояния COMPLETED . Используйте метод getPackLocation() , чтобы получить корневую папку пакета ресурсов.

Ресурсы хранятся в каталоге assets в корневом каталоге пакета ресурсов. Вы можете получить путь к каталогу assets , используя удобный метод assetsPath() . Используйте следующий метод, чтобы получить путь к определенному активу:

Котлин

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

Ява

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

Другие методы API доставки ресурсов Play

Ниже приведены некоторые дополнительные методы API, которые вы можете использовать в своем приложении.

Отменить запрос

Используйте cancel() , чтобы отменить запрос активного пакета ресурсов. Обратите внимание, что этот запрос представляет собой операцию с максимальными усилиями.

Удаление пакета ресурсов

Используйте requestRemovePack() или removePack() чтобы запланировать удаление пакета ресурсов.

Получить местоположение нескольких пакетов активов

Используйте getPackLocations() для массового запроса статуса нескольких пакетов ресурсов, который возвращает карту пакетов ресурсов и их местоположений. Карта, возвращаемая методом getPackLocations() содержит запись для каждого пакета, который в данный момент загружен и обновлен.

Следующий шаг

Протестируйте доставку ресурсов Play локально и из Google Play.