Cómo integrar Asset Delivery (Kotlin y Java)

Sigue los pasos que se indican en esta guía para acceder a los paquetes de recursos de tu app desde el código de Java.

Cómo compilar para Kotlin y Java

Usa los siguientes pasos para compilar Play Asset Delivery en el Android App Bundle de tu proyecto. No es necesario que uses Android Studio para seguir estos pasos.

  1. Actualiza la versión del complemento de Android para Gradle en el archivo build.gradle de tu proyecto a 4.0.0 o a una versión posterior.

  2. En el directorio de nivel superior de tu proyecto, crea un directorio para el paquete de recursos. Se usa el nombre de ese directorio como el nombre del paquete de recursos. Los nombres de los paquetes de recursos deben comenzar con una letra y solo pueden contener letras, números y guiones bajos.

  3. En el directorio del paquete de recursos, crea un archivo build.gradle y agrega el siguiente código. Asegúrate de especificar el nombre del paquete de recursos y solo un 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. En el archivo build.gradle de la app del proyecto, agrega el nombre de cada paquete de recursos de tu proyecto, como se muestra a continuación:

    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. En el archivo settings.gradle del proyecto, incluye todos los paquetes de recursos de tu proyecto, como se muestra a continuación:

    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. En el directorio del paquete de recursos, crea el siguiente subdirectorio: src/main/assets.

  7. Coloca los recursos en el directorio src/main/assets. Aquí también puedes crear subdirectorios. Ahora la estructura del directorio de tu app debería verse de la siguiente manera:

    • build.gradle
    • settings.gradle
    • app/
    • asset-pack-name/build.gradle
    • asset-pack-name/src/main/assets/your-asset-directories
  8. Compila el Android App Bundle con Gradle. En el paquete de aplicación que se generó, el directorio raíz ahora incluye lo siguiente:

    • asset-pack-name/manifest/AndroidManifest.xml: Configura el identificador y el modo de entrega del paquete de recursos.
    • asset-pack-name/assets/your-asset-directories: Es el directorio que contiene todos los recursos entregados como parte del paquete de recursos.

    Gradle genera el manifiesto para cada paquete de recursos y genera el directorio assets/ por ti.

  9. Opcional: Incluye la biblioteca de Play Asset Delivery si planeas usar la entrega de seguimiento rápido y a pedido.

    Groovy

    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"
    

    Kotlin

    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. Opcional: Configura el paquete de aplicación para que sea compatible con diferentes formatos de compresión de texturas.

Integra la API de Play Asset Delivery

La API de Java de Play Asset Delivery proporciona la clase AssetPackManager para solicitar paquetes de recursos, administrar descargas y acceder a los recursos. Primero, asegúrate de agregar la biblioteca de Play Asset Delivery a tu proyecto.

Esta API se implementa de acuerdo con el tipo de entrega del paquete de recursos al que deseas acceder. Estos pasos se muestran en el siguiente diagrama de flujo.

Diagrama de flujo del paquete de recursos para el lenguaje de programación Java

Figura 1: Diagrama de flujo para acceder a paquetes de recursos

Entrega durante la instalación

Los paquetes de recursos configurados como install-time están disponibles apenas se inicia la app. Usa la API de AssetManager de Java para acceder a los recursos que se entregan en este 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");

Cómo realizar entregas rápidas y a pedido

En las siguientes secciones, se muestra cómo obtener información sobre los paquetes de recursos antes de descargarlos, cómo llamar a la API para comenzar la descarga y cómo acceder a los paquetes descargados. Estas secciones se aplican a los paquetes de recursos fast-follow y on-demand.

Comprueba el estado

Cada paquete de recursos se almacena en una carpeta independiente del almacenamiento interno de la app. Usa el método getPackLocation() para determinar la carpeta raíz de un paquete de recursos. Este método devuelve los siguientes valores:

Valor que se devuelve Estado
Un objeto AssetPackLocation válido La carpeta raíz del paquete de recursos está lista para acceder de inmediato a assetsPath()
null Los paquetes de recursos o los recursos desconocidos no están disponibles

Obtén información de descarga sobre paquetes de recursos

Las apps deben divulgar el tamaño de la descarga antes de recuperar el paquete de recursos. Usa el método requestPackStates() o getPackStates() para determinar el tamaño de la descarga y si ya se está descargando el paquete.

Kotlin

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

Java

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

requestPackStates() es una función de suspensión que devuelve un objeto AssetPackStates, mientras que getPackStates() es un método asíncrono que devuelve un elemento Task<AssetPackStates>. El método packStates() de un objeto AssetPackStates devuelve un elemento Map<String, AssetPackState>. Este mapa contiene el estado de cada paquete de recursos solicitado, codificado por su nombre:

Kotlin

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

Java

Map<String, AssetPackState> AssetPackStates#packStates()

La solicitud final se muestra mediante lo siguiente:

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

Los siguientes métodos AssetPackState proporcionan el tamaño del paquete de recursos, la cantidad descargada hasta el momento (si se solicita) y la cantidad que ya se transfirió a la app:

Para obtener el estado de un paquete de recursos, usa el método status() que devuelve el estado como un número entero que corresponde a un campo constante en la clase AssetPackStatus. Un paquete de recursos que aún no está instalado tiene el estado AssetPackStatus.NOT_INSTALLED.

Si falla una solicitud, usa el método errorCode(), cuyo valor que se devuelve corresponde a un campo constante en la clase AssetPackErrorCode.

Instalación

Usa el método requestFetch() o fetch() para descargar un paquete de recursos por primera vez o realiza la llamada para que se complete la actualización de un paquete:

Kotlin

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

Java

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

Este método devuelve un objeto AssetPackStates que contiene una lista de paquetes y sus estados y tamaños de descarga iniciales. Si ya se está descargando un paquete de recursos solicitado a través de requestFetch() o fetch(), se devolverá el estado de descarga y no se iniciará ninguna descarga adicional.

Supervisa los estados de descarga

Debes implementar un objeto AssetPackStateUpdatedListener para realizar un seguimiento del progreso de la instalación de paquetes de recursos. Las actualizaciones de estado se desglosan por paquete para admitir el seguimiento del estado de paquetes de recursos individuales. Puedes comenzar a usar los paquetes de recursos disponibles antes de que se completen todas las demás descargas de la solicitud.

Kotlin

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

Java

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

Descargas grandes

Si la descarga supera los 200 MB y el usuario no está conectado a una red Wi-Fi, no se iniciará la descarga hasta que el usuario otorgue explícitamente su consentimiento para continuar con la descarga mediante una conexión de datos móviles. Del mismo modo, si la descarga es grande y el usuario pierde la conexión Wi-Fi, esta se detiene y se necesita el consentimiento explícito para continuar usando una conexión de datos móviles. Un paquete detenido tiene el estado WAITING_FOR_WIFI. Para activar el flujo de la IU y solicitar el consentimiento del usuario, usa el método showConfirmationDialog().

Ten en cuenta que si la app no llama a este método, la descarga se detiene y se reanudará automáticamente cuando el usuario vuelva a una conexión Wi-Fi.

Confirmación del usuario obligatoria

Si un paquete tiene el estado REQUIRES_USER_CONFIRMATION, la descarga no continuará hasta que el usuario acepte el diálogo que se muestra con showConfirmationDialog(). Este estado puede ocurrir cuando Play no reconoce la app, por ejemplo, si se transfirió. Ten en cuenta que llamar a showConfirmationDialog() en este caso hará que se actualice la app. Después de la actualización, deberás volver a solicitar los recursos.

A continuación, se muestra un ejemplo de implementación de un objeto de escucha:

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

También puedes usar el método getPackStates() para obtener el estado de las descargas actuales. AssetPackStates contiene el progreso de descarga, el estado de descarga y los códigos de error con fallas.

Accede a los paquetes de recursos

Puedes acceder a un paquete de recursos mediante llamadas al sistema de archivos después de que la solicitud de descarga alcanza el estado COMPLETED. Usa el método getPackLocation() para obtener la carpeta raíz del paquete de recursos.

Los recursos se almacenan en el directorio assets dentro del directorio raíz del paquete de recursos. Puedes obtener la ruta al directorio assets con el método útil assetsPath(). Usa el siguiente método para obtener la ruta a un 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;
}

Otros métodos de la API de Play Asset Delivery

Estos son algunos métodos de API adicionales que te recomendamos que uses en tu app.

Cancela una solicitud

Usa cancel() para cancelar una solicitud activa de paquete de recursos. Ten en cuenta que esta solicitud es una operación de mejor esfuerzo.

Quita un paquete de recursos

Usa el método requestRemovePack() o removePack() para programar la eliminación de un paquete de recursos.

Obtén las ubicaciones de varios paquetes de recursos

Usa getPackLocations() para consultar el estado de varios paquetes de recursos de forma masiva, lo que devuelve un mapa de los paquetes de recursos y sus ubicaciones. El mapa que devuelve getPackLocations() contiene una entrada para cada paquete que se descarga y está actualizado.

Siguiente paso

Prueba Play Asset Delivery de forma local y desde Google Play.