Используйте шаги, описанные в этом руководстве, для доступа к пакетам ресурсов вашего приложения из кода Java.
Сборка для Kotlin и Java
Используйте следующие шаги для встраивания Play Asset Delivery в Android App Bundle вашего проекта. Вам не нужно использовать Android Studio для выполнения этих шагов.
Обновите версию плагина Android Gradle в файле
build.gradle
вашего проекта до4.0.0
или более поздней.В каталоге верхнего уровня вашего проекта создайте каталог для пакета ресурсов. Это имя каталога используется в качестве имени пакета ресурсов. Имена пакетов ресурсов должны начинаться с буквы и могут содержать только буквы, цифры и подчеркивания.
В каталоге пакета ресурсов создайте файл
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 ]") } }
В файле проекта 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") }
В файле
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")
В каталоге пакета ресурсов создайте следующий подкаталог:
src/main/assets
.Поместите активы в каталог
src/main/assets
. Вы также можете создавать подкаталоги здесь. Структура каталогов для вашего приложения теперь должна выглядеть следующим образом:-
build.gradle
-
settings.gradle
-
app/
-
asset-pack-name /build.gradle
-
asset-pack-name /src/main/assets/ your-asset-directories
-
Создайте Android App Bundle с помощью Gradle . В сгенерированном app bundle корневой каталог теперь включает следующее:
-
asset-pack-name /manifest/AndroidManifest.xml
: настраивает идентификатор и режим доставки пакета ресурсов -
asset-pack-name /assets/ your-asset-directories
: Каталог, содержащий все активы, поставляемые как часть пакета активов
Gradle генерирует манифест для каждого пакета ресурсов и выводит для вас каталог
assets/
.-
(Необязательно) Включите библиотеку 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")
(Необязательно) Настройте пакет приложений для поддержки различных форматов сжатия текстур .
Интеграция с API доставки Play Asset
API Play Asset Delivery Java предоставляет класс AssetPackManager
для запроса пакетов ресурсов, управления загрузками и доступа к ресурсам. Сначала обязательно добавьте библиотеку Play Asset Delivery в свой проект.
Вы реализуете этот API в соответствии с типом доставки пакета активов, к которому вы хотите получить доступ. Эти шаги показаны на следующей блок-схеме.
Рисунок 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
в корневом каталоге asset pack. Вы можете получить путь к каталогу 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
Ниже приведены некоторые дополнительные методы API, которые вы можете использовать в своем приложении.
Отменить запрос
Используйте cancel()
для отмены активного запроса пакета активов. Обратите внимание, что этот запрос является операцией наилучшего усилия.
Удалить пакет активов
Используйте requestRemovePack()
или removePack()
чтобы запланировать удаление пакета ресурсов.
Получить местоположение нескольких пакетов активов
Используйте getPackLocations()
для массового запроса статуса нескольких пакетов активов, который возвращает карту пакетов активов и их местоположений. Карта, возвращаемая getPackLocations()
содержит запись для каждого пакета, который в данный момент загружен и актуален.
Следующий шаг
Протестируйте Play Asset Delivery локально и из Google Play.