Integrar o Asset Delivery (Kotlin e Java)

Use as etapas deste guia para acessar os pacotes de recursos do app no código Java.

Criar para Kotlin e Java

Use as etapas a seguir para criar o recurso Play Asset Delivery no Android App Bundle do seu projeto. Não é necessário usar o Android Studio para executar estas etapas.

  1. Atualize a versão do Plug-in do Android para Gradle no arquivo build.gradle do projeto para 4.0.0 ou mais recente.

  2. No diretório de nível superior do projeto, crie um diretório para o pacote de recursos. O nome do diretório é usado como nome do pacote de recursos. Os nomes de pacotes de recursos precisam começar com uma letra e só podem conter letras, números e sublinhados.

  3. No diretório do pacote de recursos, crie um arquivo build.gradle e adicione o seguinte código. Especifique o nome do pacote de recursos e apenas um tipo de entrega:

    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. No arquivo build.gradle do app do projeto, adicione o nome de cada pacote de recursos, conforme mostrado a seguir:

    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. No arquivo settings.gradle do projeto, inclua todos os pacotes de recursos, conforme mostrado a seguir:

    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. No diretório do pacote de recursos, crie o seguinte subdiretório: src/main/assets.

  7. Coloque recursos no diretório src/main/assets. Você também pode criar subdiretórios nesse local. A estrutura de diretórios do seu app agora terá a seguinte aparência:

    • build.gradle
    • settings.gradle
    • app/
    • asset-pack-name/build.gradle
    • asset-pack-name/src/main/assets/your-asset-directories
  8. Crie o Android App Bundle com o Gradle. No pacote de apps gerado, o diretório de nível raiz agora inclui o seguinte:

    • asset-pack-name/manifest/AndroidManifest.xml: configura o identificador e o modo de entrega do pacote de recursos
    • asset-pack-name/assets/your-asset-directories: diretório que contém todos os recursos enviados como parte do pacote de recursos

    O Gradle gera o manifesto para cada pacote de recursos e gera o diretório assets/ para você.

  9. (Opcional) Inclua a biblioteca Play Asset Delivery se você quiser usar a entrega rápida e sob demanda.

    Groovy

    implementation "com.google.android.play:asset-delivery:2.2.1"
    // For Kotlin use asset-delivery-ktx
    implementation "com.google.android.play:asset-delivery-ktx:2.2.1"
    

    Kotlin

    implementation("com.google.android.play:asset-delivery:2.2.1")
    // For Kotlin use core-ktx
    implementation("com.google.android.play:asset-delivery-ktx:2.2.1")
    

  10. (Opcional) Configure seu pacote de apps para oferecer suporte a diferentes formatos de compactação de textura.

Integrar com a API Play Asset Delivery

A API Play Asset Delivery para Java fornece a classe AssetPackManager para solicitar pacotes de recursos, gerenciar downloads e acessar os recursos. Primeiro, adicione a biblioteca Play Asset Delivery ao seu projeto.

Implemente essa API de acordo com o tipo de entrega do pacote de recursos que você quer acessar. Essas etapas são mostradas no fluxograma a seguir.

Diagrama de fluxo de pacotes de recursos para a linguagem de programação Java

Figura 1. Diagrama de fluxo para acessar pacotes de recursos

Entrega no momento da instalação

Os pacotes de recursos configurados como install-time estão imediatamente disponíveis na inicialização do app. Use a API AssetManager do Java para acessar recursos veiculados nesse modo:

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

Entrega rápida e sob demanda

As seções a seguir mostram como acessar informações sobre pacotes de recursos antes de fazer o download deles, como chamar a API para iniciar o download e como acessá-los após o download. Essas seções se aplicam aos pacotes de recursos fast-follow e on-demand.

Verificar status

Cada pacote de recursos é armazenado em uma pasta separada no armazenamento interno do app. Use o método getPackLocation() para determinar a pasta raiz de um pacote de recursos. Esse método retorna os seguintes valores:

Valor de retorno Status
Um objeto AssetPackLocation válido A pasta raiz do pacote de recursos está pronta para acesso imediato em assetsPath()
null Pacotes de recursos desconhecidos ou indisponíveis

Receber informações de download sobre pacotes de recursos

Os apps precisam divulgar o tamanho do download antes de buscar o pacote de recursos. Use o método requestPackStates() ou getPackStates() para determinar o tamanho do download e se o pacote já está sendo transferido por download.

Kotlin

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

Java

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

requestPackStates() é uma função de suspensão que retorna um objeto AssetPackStates, e getPackStates() é um método assíncrono que retorna um Task<AssetPackStates>. O método packStates() de um objeto AssetPackStates retorna um Map<String, AssetPackState>. Esse mapa contém o estado de cada pacote de recursos solicitado, codificado pelo nome:

Kotlin

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

Java

Map<String, AssetPackState> AssetPackStates#packStates()

A solicitação final é mostrada por:

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

Os métodos AssetPackState abaixo fornecem o tamanho do pacote de recursos, o valor transferido por download até o momento (se solicitado) e o valor já transferido ao app.

Para saber o status de um pacote de recursos, use o método status(), que retorna o status como um número inteiro que corresponde a um campo de constante na classe AssetPackStatus. Um pacote de recursos ainda não instalado tem o status AssetPackStatus.NOT_INSTALLED.

Se uma solicitação falhar, use o método errorCode(), cujo valor de retorno corresponde a um campo de constante na classe AssetPackErrorCode.

Instalar

Use os métodos requestFetch() ou fetch() para fazer o download de um pacote de recursos pela primeira vez ou chame a atualização de um pacote de recursos para concluir:

Kotlin

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

Java

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

Esse método retorna um objeto AssetPackStates contendo uma lista de pacotes e os estados e tamanhos iniciais de download deles. Se um download de pacote de recurso solicitado pelo método requestFetch() ou fetch() já estiver sendo feito, o status do download é retornado e nenhum outro download vai ser iniciado.

Monitorar estados de download

Implemente um AssetPackStateUpdatedListener para monitorar o progresso da instalação de pacotes de recursos. As atualizações de status são divididas por pacote para auxiliar no monitoramento do status de pacotes de recursos individuais. Você pode começar a usar os pacotes de recursos disponíveis antes que todos os outros downloads da sua solicitação sejam concluídos.

Kotlin

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

Java

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

Downloads grandes

Se o download for maior que 200 MB e o usuário não estiver conectado ao Wi-Fi, o download não será iniciado até que o usuário dê explicitamente o consentimento para prosseguir com o download usando uma conexão de dados móveis. Da mesma forma, se o download for grande e o usuário perder a conexão ao Wi-Fi, o download será pausado e o consentimento explícito será necessário para continuar usando uma conexão de dados móveis. Um pacote pausado tem o estado WAITING_FOR_WIFI. Para acionar o fluxo da interface para solicitar o consentimento do usuário, use o método showConfirmationDialog().

Se o app não chamar esse método, o download será pausado e retomado automaticamente somente quando o usuário voltar a se conectar por Wi-Fi.

Confirmação obrigatória do usuário

Se um pacote tiver o status REQUIRES_USER_CONFIRMATION, o download não continuará até que o usuário aceite a caixa de diálogo mostrada com showConfirmationDialog(). Esse status pode ocorrer quando o app não é reconhecido pelo Google Play, por exemplo, se ele foi transferido por sideload. Nesse caso, chamar showConfirmationDialog() vai fazer com que o app seja atualizado. Após a atualização, você vai precisar solicitar os recursos novamente.

Este é um exemplo de implementação de um listener:

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

Se preferir, use o método getPackStates() para ver o status dos downloads atuais. AssetPackStates contém o progresso do download, o status do download e todos os códigos de erro de falha.

Acessar pacotes de recursos

É possível acessar um pacote de recursos usando chamadas do sistema de arquivos após a solicitação de download chegar ao estado COMPLETED. Use o método getPackLocation() para acessar a pasta raiz do pacote de recursos.

Os recursos são armazenados no diretório assets dentro do diretório raiz do pacote de recursos. Você pode acessar o caminho para o diretório assets usando o método de conveniência assetsPath(). Use o método abaixo para acessar o caminho de um recurso específico:

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

Outros métodos da API Play Asset Delivery

Mostramos abaixo alguns outros métodos da API que podem ser usados no seu app.

Cancelar solicitação

Use cancel() para cancelar uma solicitação ativa de pacote de recursos. Observe que essa solicitação é uma operação de melhor esforço.

Remover um pacote de recursos

Use requestRemovePack() ou removePack() para programar a remoção de um pacote de recursos.

Acessar locais de vários pacotes de recursos

Use getPackLocations() para consultar o status de vários pacotes de recursos em massa. Isso retorna um mapa de pacotes de recursos e os locais deles. O mapa retornado por getPackLocations() contém uma entrada para cada pacote que está atualmente salvo e atualizado.

A seguir

Teste o Play Asset Delivery localmente e no Google Play.