Интегрируйте доставку ресурсов (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. В файле проекта app 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 Asset Delivery, если вы планируете использовать быструю доставку и доставку по запросу.

    Круто

    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"

    Котлин

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

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

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

Этот 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() . Этот статус может возникать, когда приложение не распознаётся Google 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 Asset Delivery

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

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

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

Удалить пакет активов

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

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

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

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

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