Google se compromete a impulsar la igualdad racial para las comunidades afrodescendientes. Obtén información al respecto.

Descarga de módulos con la biblioteca de Play Core

Con Dynamic Delivery de Google Play, tu app puede descargar módulos de funciones dinámicas a pedido en los dispositivos con Android 5.0 (API nivel 21) y versiones posteriores. Tu app solo necesita llamar a las API en la biblioteca de Play Core para descargar e instalar esos módulos según sea necesario, y Google Play Store solo instala el código y los recursos necesarios para ese módulo en el dispositivo. También puedes usar esta API a fin de descargar módulos a pedido para tus Apps instantáneas Android.

Si deseas obtener más información sobre cómo agregar módulos de funciones dinámicas a tu proyecto y configurarlos para que estén disponibles a pedido, lee Cómo crear un módulo de funciones dinámicas.

Además, después de leer esta guía, consulta la API en acción. Para ello, prueba la app de muestra de la API Play Core y obtén información sobre cómo admitir las actualizaciones en la app.

Por último, antes de publicar tu app, prueba tu paquete de aplicación para verificar que la funcionalidad a pedido se ejecute correctamente.

Cómo incluir la biblioteca de Play Core en tu proyecto

Antes de comenzar a usar la biblioteca de Play Core, debes importarla al módulo de tu app como una dependencia de Gradle, como se muestra a continuación:

    // In your app’s build.gradle file:
    ...
    dependencies {
        // This dependency is downloaded from the Google’s Maven repository.
        // So, make sure you also include that repository in your project's build.gradle file.
        implementation 'com.google.android.play:core:1.6.4'
        ...
    }
    

Cómo solicitar un módulo a pedido

Cuando tu app necesita usar un módulo de funciones dinámicas, puede solicitar uno mientras está en primer plano mediante la clase SplitInstallManager. Cuando tu app realiza una solicitud, debe especificar el nombre del módulo como lo define el elemento split en el manifiesto del módulo de objetivo. Cuando creas un módulo de funciones dinámicas mediante Android Studio, el sistema de compilación usa el nombre de módulo que indicas para agregar esta propiedad en el manifiesto del módulo en el tiempo de compilación. Si deseas obtener más información, lee sobre los manifiestos del módulo de funciones dinámicas.

Por ejemplo, imagina una app que tiene un módulo a pedido para capturar y enviar mensajes con imágenes mediante la cámara del dispositivo y este módulo a pedido tiene split="pictureMessages" especificado en su manifiesto. En el siguiente ejemplo, se usa SplitInstallManager para solicitar el módulo de pictureMessages (además del módulo adicional para algunos filtros promocionales):

Kotlin

    // Creates an instance of SplitInstallManager.
    val splitInstallManager = SplitInstallManagerFactory.create(context)

    // Creates a request to install a module.
    val request =
        SplitInstallRequest
            .newBuilder()
            // You can download multiple on demand modules per
            // request by invoking the following method for each
            // module you want to install.
            .addModule("pictureMessages")
            .addModule("promotionalFilters")
            .build()

    splitInstallManager
        // Submits the request to install the module through the
        // asynchronous startInstall() task. Your app needs to be
        // in the foreground to submit the request.
        .startInstall(request)
        // You should also be able to gracefully handle
        // request state changes and errors. To learn more, go to
        // the section about how to Monitor the request state.
        .addOnSuccessListener { sessionId -> ... }
        .addOnFailureListener { exception ->  ... }
    

Java

    // Creates an instance of SplitInstallManager.
    SplitInstallManager splitInstallManager =
        SplitInstallManagerFactory.create(context);

    // Creates a request to install a module.
    SplitInstallRequest request =
        SplitInstallRequest
            .newBuilder()
            // You can download multiple on demand modules per
            // request by invoking the following method for each
            // module you want to install.
            .addModule("pictureMessages")
            .addModule("promotionalFilters")
            .build();

    splitInstallManager
        // Submits the request to install the module through the
        // asynchronous startInstall() task. Your app needs to be
        // in the foreground to submit the request.
        .startInstall(request)
        // You should also be able to gracefully handle
        // request state changes and errors. To learn more, go to
        // the section about how to Monitor the request state.
        .addOnSuccessListener(sessionId -> { ... })
        .addOnFailureListener(exception -> { ... });
    

Cuando tu app solicita un módulo a pedido, la biblioteca de Play Core utiliza una estrategia de "activar y olvidar". Es decir, envía la solicitud para descargar el módulo a la plataforma, pero no supervisa si la instalación se realizó correctamente. Para avanzar en el recorrido del usuario después de la instalación o para administrar errores correctamente, asegúrate de supervisar el estado de la solicitud.

Nota: Es posible solicitar un módulo de funciones dinámicas que ya está instalado en el dispositivo. La API considera inmediatamente que se completó la solicitud si detecta que el módulo ya está instalado. Además, después de instalar un módulo, Google Play lo actualiza automáticamente. Es decir, cuando subes una versión nueva de tu paquete de aplicación, la plataforma actualiza todos los APK instalados que pertenecen a tu app. Si deseas obtener más información, consulta Cómo administrar actualizaciones de apps.

Para obtener acceso inmediato al código y los recursos del módulo, debes habilitar SplitCompat para tu app. Ten en cuenta que SplitCompat no es necesario para las Apps instantáneas Android, ya que siempre tienen acceso inmediato a los módulos de funciones.

Cómo posponer la instalación de los módulos a pedido

Si no necesitas que tu app descargue e instale inmediatamente un módulo a pedido, puedes postergar la instalación para cuando la app esté en segundo plano. Por ejemplo, si quieres precargar material promocional para un lanzamiento posterior de tu app.

Puedes especificar que un módulo se descargue más adelante mediante el método deferredInstall(), como se muestra a continuación. Además, a diferencia de SplitInstallManager.startInstall(), tu app no necesita estar en primer plano para iniciar la solicitud de una instalación postergada.

Kotlin

    // Requests an on demand module to be downloaded when the app enters
    // the background. You can specify more than one module at a time.
    splitInstallManager.deferredInstall(listOf("promotionalFilters"))
    

Java

    // Requests an on demand module to be downloaded when the app enters
    // the background. You can specify more than one module at a time.
    splitInstallManager.deferredInstall(Arrays.asList("promotionalFilters"));
    

Las solicitudes de instalaciones postergadas se incluyen en la categoría de mejor esfuerzo y no puedes realizar un seguimiento de su progreso. Por lo tanto, antes de intentar acceder a un módulo que especificaste para la instalación diferida, debes verificar que el módulo se haya instalado. Si necesitas que el módulo esté disponible inmediatamente, usa SplitInstallManager.startInstall() en su lugar para solicitarlo, como se muestra en la sección anterior.

Cómo supervisar el estado de la solicitud

Para poder actualizar una barra de progreso, activar un intent después de la instalación o manejar correctamente un error de solicitud, debes detectar actualizaciones de estado desde la tarea SplitInstallManager.startInstall() asíncrona. Antes de comenzar a recibir actualizaciones para tu solicitud de instalación, registra un objeto de escucha y obtén el ID de sesión de la solicitud, como se muestra a continuación.

Kotlin

    // Initializes a variable to later track the session ID for a given request.
    var mySessionId = 0

    // Creates a listener for request status updates.
    val listener = SplitInstallStateUpdatedListener { state ->
        if (state.sessionId() == mySessionId) {
          // Read the status of the request to handle the state update.
        }
    }

    // Registers the listener.
    splitInstallManager.registerListener(listener)

    ...

    splitInstallManager
        .startInstall(request)
        // When the platform accepts your request to download
        // an on demand module, it binds it to the following session ID.
        // You use this ID to track further status updates for the request.
        .addOnSuccessListener { sessionId -> mySessionId = sessionId }
        // You should also add the following listener to handle any errors
        // processing the request.
        .addOnFailureListener { exception ->
            // Handle request errors.
        }

    // When your app no longer requires further updates, unregister the listener.
    splitInstallManager.unregisterListener(listener)
    

Java

    // Initializes a variable to later track the session ID for a given request.
    int mySessionId = 0;

    // Creates a listener for request status updates.
    SplitInstallStateUpdatedListener listener = state -> {
        if (state.sessionId() == mySessionId) {
          // Read the status of the request to handle the state update.
        }
    };

    // Registers the listener.
    splitInstallManager.registerListener(listener);

    ...

    splitInstallManager
        .startInstall(request)
        // When the platform accepts your request to download
        // an on demand module, it binds it to the following session ID.
        // You use this ID to track further status updates for the request.
        .addOnSuccessListener(sessionId -> { mySessionId = sessionId; })
        // You should also add the following listener to handle any errors
        // processing the request.
        .addOnFailureListener(exception -> {
            // Handle request errors.
        });

    // When your app no longer requires further updates, unregister the listener.
    splitInstallManager.unregisterListener(listener);
    

Cómo manejar errores de solicitud

Para manejar correctamente las fallas, descarga o instala un módulo a través de addOnFailureListener(), como se indica a continuación:

Kotlin

    splitInstallManager
        .startInstall(request)
        .addOnFailureListener { exception ->
            when ((exception as SplitInstallException).errorCode) {
                SplitInstallErrorCode.NETWORK_ERROR -> {
                    // Display a message that requests the user to establish a
                    // network connection.
                }
                SplitInstallErrorCode.ACTIVE_SESSIONS_LIMIT_EXCEEDED -> checkForActiveDownloads()
                ...
            }
        }

    fun checkForActiveDownloads() {
        splitInstallManager
            // Returns a SplitInstallSessionState object for each active session as a List.
            .sessionStates
            .addOnCompleteListener { task ->
                if (task.isSuccessful) {
                    // Check for active sessions.
                    for (state in task.result) {
                        if (state.status() == SplitInstallSessionStatus.DOWNLOADING) {
                            // Cancel the request, or request a deferred installation.
                        }
                    }
                }
            }
    }
    

Java

    splitInstallManager
        .startInstall(request)
        .addOnFailureListener(exception -> {
            switch (((SplitInstallException) exception).getErrorCode()) {
                case SplitInstallErrorCode.NETWORK_ERROR:
                    // Display a message that requests the user to establish a
                    // network connection.
                    break;
                case SplitInstallErrorCode.ACTIVE_SESSIONS_LIMIT_EXCEEDED:
                    checkForActiveDownloads();
                ...
        });

    void checkForActiveDownloads() {
        splitInstallManager
            // Returns a SplitInstallSessionState object for each active session as a List.
            .getSessionStates()
            .addOnCompleteListener( task -> {
                if (task.isSuccessful()) {
                    // Check for active sessions.
                    for (SplitInstallSessionState state : task.getResult()) {
                        if (state.status() == SplitInstallSessionStatus.DOWNLOADING) {
                            // Cancel the request, or request a deferred installation.
                        }
                    }
                }
            });
    }
    

En la siguiente tabla, se describen los estados de error que es posible que tu app deba manejar:

Código de error Descripción Acción sugerida
ACTIVE_SESSIONS_LIMIT_EXCEEDED Se rechaza la solicitud porque, actualmente, se está descargando, al menos, una solicitud existente. Verifica si todavía se están descargando solicitudes, como se muestra en el ejemplo anterior.
MODULE_UNAVAILABLE Google Play no encuentra el módulo que se solicitó en función de la versión instalada actualmente de la app, el dispositivo y la cuenta de Google Play del usuario. Si el usuario no tiene acceso al módulo, debes notificarlo al respecto.
INVALID_REQUEST Google Play recibió la solicitud, pero esta no es válida. Verifica que la información incluida en la solicitud esté completa y sea precisa.
SESSION_NOT_FOUND No se encontró una sesión para un ID de sesión determinado. Si intentas supervisar el estado de una solicitud a partir de su ID de sesión, asegúrate de que este sea correcto.
API_NOT_AVAILABLE El dispositivo actual no admite la biblioteca de Play Core. Es decir, el dispositivo no puede descargar ni instalar funciones a pedido. Para los dispositivos con Android 4.4 (API nivel 20) o versiones anteriores, debes incluir módulos de funciones dinámicas en el tiempo de instalación. Para ello, usa la propiedad de manifiesto dist:fusing. Si deseas obtener más información, lee sobre el manifiesto del módulo de funciones dinámicas.
ACCESS_DENIED La app no puede registrar la solicitud debido a que los permisos no son suficientes. Por lo general, esto sucede cuando la app está en segundo plano. Intenta ejecutar la solicitud cuando la app vuelva al primer plano.
NETWORK_ERROR Falló la solicitud debido a un error de red. Pídele al usuario que establezca una conexión de red o que se conecte a una diferente.
INCOMPATIBLE_WITH_EXISTING_SESSION La solicitud contiene uno o más módulos que ya se solicitaron pero que todavía no se instalaron. Crea una solicitud nueva que no incluya módulos que tu app ya solicitó o espera a que todos los módulos solicitados actualmente terminen de instalarse antes de volver a intentarlo.

Ten en cuenta que solicitar un módulo que ya se instaló no soluciona un error.

SERVICE_DIED El servicio responsable de manejar la solicitud no está disponible. Vuelve a intentar ejecutar la solicitud.

Este código de error se expone como una actualización a tu SplitInstallStateUpdatedListener con el estado FAILED y el ID de sesión -1.

Si un usuario solicita descargar un módulo a pedido y se produce un error, puedes mostrar un diálogo en el que se brinden dos opciones para el usuario: Try again (para volver a intentar la solicitud) y Cancel (para abandonar la solicitud). Para obtener asistencia adicional, también debes proporcionar un vínculo de ayuda que dirija a los usuarios al Centro de ayuda de Google Play.

Cómo manejar actualizaciones de estado

Después de registrar un objeto de escucha y grabar el ID de sesión de tu solicitud, usa StateUpdatedListener.onStateUpdate() a fin de manejar los cambios de estado, como se muestra a continuación.

Kotlin

    override fun onStateUpdate(state : SplitInstallSessionState) {
        if (state.status() == SplitInstallSessionStatus.FAILED
            && state.errorCode() == SplitInstallErrorCode.SERVICE_DIES) {
           // Retry the request.
           return
        }
        if (state.sessionId() == mySessionId) {
            when (state.status()) {
                SplitInstallSessionStatus.DOWNLOADING -> {
                  val totalBytes = state.totalBytesToDownload()
                  val progress = state.bytesDownloaded()
                  // Update progress bar.
                }
                SplitInstallSessionStatus.INSTALLED -> {

                  // After a module is installed, you can start accessing its content or
                  // fire an intent to start an activity in the installed module.
                  // For other use cases, see access code and resources from installed modules.

                  // If the request is an on demand module for an Android Instant App
                  // running on Android 8.0 (API level 26) or higher, you need to
                  // update the app context using the SplitInstallHelper API.
                }
            }
        }
    }
    

Java

    @Override
    public void onStateUpdate(SplitInstallSessionState state) {
        if (state.status() == SplitInstallSessionStatus.FAILED
            && state.errorCode() == SplitInstallErrorCode.SERVICE_DIES) {
           // Retry the request.
           return;
        }
        if (state.sessionId() == mySessionId) {
            switch (state.status()) {
                case SplitInstallSessionStatus.DOWNLOADING:
                  int totalBytes = state.totalBytesToDownload();
                  int progress = state.bytesDownloaded();
                  // Update progress bar.
                  break;

                case SplitInstallSessionStatus.INSTALLED:

                  // After a module is installed, you can start accessing its content or
                  // fire an intent to start an activity in the installed module.
                  // For other use cases, see access code and resources from installed modules.

                  // If the request is an on demand module for an Android Instant App
                  // running on Android 8.0 (API level 26) or higher, you need to
                  // update the app context using the SplitInstallHelper API.
            }
        }
    }
    

Los estados posibles de tu solicitud de instalación se describen en la siguiente tabla.

Estado de solicitud Descripción Acción sugerida
PENDIENTE Se aceptó la solicitud y la descarga debería comenzar pronto. Inicializa los componentes de IU, como una barra de progreso, para brindar los comentarios del usuario sobre la descarga.
REQUIRES_USER_CONFIRMATION La descarga requiere la confirmación del usuario. Lo más probable es que se deba al tamaño de la descarga, que supera los 10 MB. Pídele al usuario que acepte la solicitud de descarga. Si deseas obtener más información, ve a la sección sobre cómo obtener información del usuario.
DESCARGANDO La descarga está en curso. Si agregas una barra de progreso para la descarga, usa los métodos SplitInstallSessionState.bytesDownloaded() y SplitInstallSessionState.totalBytesToDownload() para actualizar la IU (consulta la muestra de código arriba de esta tabla).
DESCARGADO El dispositivo descargó el módulo, pero la instalación todavía no comenzó. Las apps deberían habilitar SplitCompat para tener acceso inmediato a los módulos descargados y evitar ver este estado. De lo contrario, la descarga pasa a INSTALLED y tu app accede a su código y sus recursos solo después de que la app entra en segundo plano.
INSTALANDO El dispositivo está instalando el módulo. Actualiza la barra de progreso. Por lo general, este estado es corto.
INSTALADO Se instaló el módulo en el dispositivo. Accede al código y a los recursos en el módulo para continuar el recorrido del usuario.

Si el módulo es para una App instantánea Android que ejecuta Android 8.0 (API nivel 26) o versiones posteriores, usa splitInstallHelper para actualizar los componentes de la app con el módulo nuevo.

ERROR Falló la solicitud antes de que se pudiera instalar el módulo en el dispositivo. Pídele al usuario que vuelva a enviar la solicitud o que la cancele.
CANCELANDO El dispositivo está en proceso de cancelar la solicitud. Para obtener más información, ve a la sección sobre cómo cancelar una solicitud de instalación.
CANCELADA Se canceló la solicitud.

Cómo obtener la confirmación del usuario

En algunos casos, es posible que Google Play requiera la confirmación del usuario antes de completar una solicitud de descarga. Por ejemplo, si una solicitud requiere una descarga de gran tamaño y el dispositivo usa datos móviles. En estos casos, el estado de la solicitud informa REQUIRES_USER_CONFIRMATION y tu app necesita obtener la confirmación del usuario para que el dispositivo pueda descargar e instalar los módulos en la solicitud. Para obtener la confirmación, tu app debe pedirle al usuario lo siguiente:

Kotlin

    override fun onSessionStateUpdate(state: SplitInstallSessionState) {
        if (state.status() == SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION) {
            // Displays a dialog for the user to either “Download”
            // or “Cancel” the request.
            splitInstallManager.startConfirmationDialogForResult(
              state,
              /* activity = */ this,
              // You use this request code to later retrieve the user's decision.
              /* requestCode = */ MY_REQUEST_CODE)
        }
        ...
     }
    

Java

    @Override void onSessionStateUpdate(SplitInstallSessionState state) {
        if (state.status() == SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION) {
            // Displays a dialog for the user to either “Download”
            // or “Cancel” the request.
            splitInstallManager.startConfirmationDialogForResult(
              state,
              /* activity = */ this,
              // You use this request code to later retrieve the user's decision.
              /* requestCode = */ MY_REQUEST_CODE);
        }
        ...
     }
    

El estado de la solicitud se actualiza según la respuesta del usuario:

  • Si el usuario selecciona "Download", el estado de la solicitud cambia a PENDING y la descarga continúa.
  • Si el usuario selecciona "Cancel", el estado de la solicitud cambia a CANCELED.
  • Si el usuario no hace una selección antes de que se destruya el diálogo, el estado de la solicitud permanece como REQUIRES_USER_CONFIRMATION. Tu app puede volver a pedirle al usuario que complete la solicitud.

Para recibir una devolución de llamada con la respuesta del usuario, utiliza onActivityResult(), como se muestra a continuación.

Kotlin

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
      if (requestCode == MY_REQUEST_CODE) {
        // Handle the user's decision. For example, if the user selects "Cancel",
        // you may want to disable certain functionality that depends on the module.
      }
    }
    

Java

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
      if (requestCode == MY_REQUEST_CODE) {
        // Handle the user's decision. For example, if the user selects "Cancel",
        // you may want to disable certain functionality that depends on the module.
      }
    }
    

Cómo cancelar una solicitud de instalación

Si tu app necesita cancelar una solicitud antes de que se instale, puede invocar el método cancelInstall() mediante el ID de sesión de la solicitud, como se muestra a continuación.

Kotlin

    SplitInstallManager
        // Cancels the request for the given session ID.
        .cancelInstall(mySessionId)
    

Java

    SplitInstallManager
        // Cancels the request for the given session ID.
        .cancelInstall(mySessionId);
    

Cómo acceder inmediatamente a los módulos

Para acceder inmediatamente al código y los recursos desde un módulo descargado (es decir, antes del reinicio de una app), debes habilitar la biblioteca de SplitCompat para tu app y cada actividad en los módulos de funciones dinámicas que descargue.

Sin embargo, ten en cuenta que la plataforma cuenta con las siguientes restricciones para acceder al contenido de un módulo antes del reinicio de una app:

  • La plataforma no puede aplicar ninguna entrada nueva del manifiesto que ingrese el módulo.
  • La plataforma no puede acceder a los recursos del módulo para los componentes de la IU del sistema, como las notificaciones. Si necesitas usar esos recursos inmediatamente, puedes incluirlos en el módulo de base de tu app.

Cómo habilitar SplitCompat

Si quieres que tu app acceda inmediatamente al código y los recursos desde un módulo descargado, debes habilitar SplitCompat mediante solo uno de los métodos que se describen a continuación.

Después de habilitar SplitCompat para tu app, también debes habilitar SplitCompat para cada actividad en los módulos de funciones dinámicas a los que quieres que tu app tenga acceso inmediato.

Cómo declarar SplitCompatApplication en el manifiesto

La manera más simple de habilitar SplitCompat consiste en declarar SplitCompatApplication como la subclase de Application en el manifiesto de tu app, como se muestra a continuación:

<application
        ...
        android:name="com.google.android.play.core.splitcompat.SplitCompatApplication">
    </application>
    

Después de instalar la app en un dispositivo, puedes acceder automáticamente al código y los recursos de los módulos de funciones dinámicas descargados.

Cómo invocar SplitCompat en el tiempo de ejecución

También puedes habilitar SplitCompat en actividades o servicios específicos en el tiempo de ejecución. Es necesario habilitar SplitCompat de esta manera para iniciar las actividades incluidas en los módulos de funciones inmediatamente después de la instalación del módulo. Para ello, anula attachBaseContext como se muestra a continuación.

Si tienes una clase Application personalizada, haz que extienda SplitCompatApplication a fin de habilitar SplitCompat para tu app, como se muestra a continuación:

Kotlin

    class MyApplication : SplitCompatApplication() {
        ...
    }
    

Java

    public class MyApplication extends SplitCompatApplication {
        ...
    }
    

SplitCompatApplication simplemente anula ContextWrapper.attachBaseContext() para incluir SplitCompat.install(Context applicationContext). Si no deseas que tu clase Application extienda SplitCompatApplication, puedes anular el método attachBaseContext() de forma manual, como se muestra a continuación:

Kotlin

    override fun attachBaseContext(base: Context) {
        super.attachBaseContext(base)
        // Emulates installation of future on demand modules using SplitCompat.
        SplitCompat.install(this)
    }
    

Java

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        // Emulates installation of future on demand modules using SplitCompat.
        SplitCompat.install(this);
    }
    

Si tu módulo a pedido es compatible con las apps instantáneas y las apps instaladas, puedes invocar SplitCompat de manera condicional, como se muestra a continuación:

Kotlin

    override fun attachBaseContext(base: Context) {
        super.attachBaseContext(base)
        if (!InstantApps.isInstantApp(this)) {
            SplitCompat.install(this)
        }
    }
    

Java

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        if (!InstantApps.isInstantApp(this)) {
            SplitCompat.install(this);
        }
    }
    

Cómo habilitar SplitCompat para las actividades del módulo

Después de habilitar SplitCompat para tu app de base, debes habilitar SplitCompat para cada actividad que tu app descargue en un módulo de funciones dinámicas. Para ello, usa el método SplitCompat.installActivity() de la siguiente manera:

Kotlin

    override fun attachBaseContext(base: Context) {
        super.attachBaseContext(base)
        // Emulates installation of on demand modules using SplitCompat.
        SplitCompat.installActivity(this)
    }
    

Java

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        // Emulates installation of on demand modules using SplitCompat.
        SplitCompat.installActivity(this);
    }
    

Cómo acceder al código y los recursos desde los módulos instalados

Siempre que habilites SplitCompat para el contexto de tu aplicación de base y las actividades en tu módulo de funciones dinámicas, después de que una solicitud de un módulo a pedido informe INSTALLED, puedes comenzar a usar su código y sus recursos como si fuesen parte del APK de base.

Si deseas acceder a elementos o recursos que existen en el módulo recién instalado desde un módulo instalado diferente de tu app, debes hacerlo mediante el contexto de la aplicación. El contexto del componente que intenta acceder a los recursos todavía no estará actualizado. Como alternativa, puedes volver a crear ese componente o instalar SplitCompat por encima después de la instalación del módulo de funciones dinámicas.

Además, no almacenes en caché los objetos ApplicationInfo de Android, su contenido o sus objetos que los incluyan dentro de tu app. Siempre debes obtener estos objetos desde el contexto de una app, según sea necesario. Si almacenas esos objetos en caché, podrías provocar que la app falle cuando instale una versión dividida o versiones más recientes de Android.

Cómo acceder a Apps instantáneas Android instaladas

Después de que se informe un módulo de App instantánea Android como INSTALLED, puedes acceder a su código y sus recursos mediante el contexto actualizado de una app. Un contexto que tu app crea antes de instalar un módulo (por ejemplo, uno que ya está instalado en una variable) no incluye el contenido del módulo nuevo. Sin embargo, un contexto actualizado sí lo incluye y lo puedes obtener, por ejemplo, mediante createPackageContext.

Kotlin

    // Generate a new context as soon as a request for a new module
    // reports as INSTALLED.
    override fun onStateUpdate(state: SplitInstallSessionState ) {
        if (state.sessionId() == mySessionId) {
            when (state.status()) {
                ...
                SplitInstallSessionStatus.INSTALLED -> {
                    val newContext = context.createPackageContext(context.packageName, 0)
                    // If you use AssetManager to access your app’s raw asset files, you’ll need
                    // to generate a new AssetManager instance from the updated context.
                    val am = newContext.assets
                }
            }
        }
    }
    

Java

    // Generate a new context as soon as a request for a new module
    // reports as INSTALLED.
    @Override
    public void onStateUpdate(SplitInstallSessionState state) {
        if (state.sessionId() == mySessionId) {
            switch (state.status()) {
                ...
                case SplitInstallSessionStatus.INSTALLED:
                    Context newContext = context.createPackageContext(context.getPackageName(), 0);
                    // If you use AssetManager to access your app’s raw asset files, you’ll need
                    // to generate a new AssetManager instance from the updated context.
                    AssetManager am = newContext.getAssets();
            }
        }
    }
    

Apps instantáneas Android en Android 8.0 y versiones posteriores

Cuando solicitas un módulo a pedido para una App instantánea Android en Android 8.0 (API nivel 26) y versiones posteriores, después de que una solicitud de instalación se informa como INSTALLED, debes actualizar la app con el contexto de módulo nuevo a través de una llamada a SplitInstallHelper.updateAppInfo(Context context). De lo contrario, la app no está al tanto del código y los recursos del módulo. Después de actualizar los metadatos de la app, debes cargar el contenido del módulo durante el próximo evento de subproceso principal. Para ello, invoca un Handler nuevo, como se indica a continuación:

Kotlin

    override fun onStateUpdate(state: SplitInstallSessionState ) {
        if (state.sessionId() == mySessionId) {
            when (state.status()) {
                ...
                SplitInstallSessionStatus.INSTALLED -> {
                    // You need to perform the following only for Android Instant Apps
                    // running on Android 8.0 (API level 26) and higher.
                    if (BuildCompat.isAtLeastO()) {
                        // Updates the app’s context with the code and resources of the
                        // installed module.
                        SplitInstallHelper.updateAppInfo(context)
                        Handler().post {
                            // Loads contents from the module using AssetManager
                            val am = context.assets
                            ...
                        }
                    }
                }
            }
        }
    }
    

Java

    @Override
    public void onStateUpdate(SplitInstallSessionState state) {
        if (state.sessionId() == mySessionId) {
            switch (state.status()) {
                ...
                case SplitInstallSessionStatus.INSTALLED:
                // You need to perform the following only for Android Instant Apps
                // running on Android 8.0 (API level 26) and higher.
                if (BuildCompat.isAtLeastO()) {
                    // Updates the app’s context with the code and resources of the
                    // installed module.
                    SplitInstallHelper.updateAppInfo(context);
                    new Handler().post(new Runnable() {
                        @Override public void run() {
                            // Loads contents from the module using AssetManager
                            AssetManager am = context.getAssets();
                            ...
                        }
                    });
                }
            }
        }
    }
    

Cómo cargar bibliotecas C/C++

Si deseas cargar bibliotecas C/C++ desde un módulo que el dispositivo ya descargó, usa SplitInstallHelper.loadLibrary(Context context, String libName), como se muestra a continuación:

Kotlin

    override fun onStateUpdate(state: SplitInstallSessionState) {
        if (state.sessionId() == mySessionId) {
            when (state.status()) {
                SplitInstallSessionStatus.INSTALLED -> {
                    // Updates the app’s context as soon as a module is installed.
                    val newContext = context.createPackageContext(context.packageName, 0)
                    // To load C/C++ libraries from an installed module, use the following API
                    // instead of System.load().
                    SplitInstallHelper.loadLibrary(newContext, “my-cpp-lib”)
                    ...
                }
            }
        }
    }
    

Java

    public void onStateUpdate(SplitInstallSessionState state) {
        if (state.sessionId() == mySessionId) {
            switch (state.status()) {
                case SplitInstallSessionStatus.INSTALLED:
                    // Updates the app’s context as soon as a module is installed.
                    Context newContext = context.createPackageContext(context.getPackageName(), 0);
                    // To load C/C++ libraries from an installed module, use the following API
                    // instead of System.load().
                    SplitInstallHelper.loadLibrary(newContext, “my-cpp-lib”);
                    ...
            }
        }
    }
    

Cómo administrar módulos instalados

Para verificar qué módulos de funciones dinámicas están instalados actualmente en el dispositivo, puedes llamar a SplitInstallManager.getInstalledModules(), que muestra un Set<String> de los nombres de los módulos instalados, como se muestra a continuación.

Kotlin

    val installedModules: Set<String> = splitInstallManager.installedModules
    

Java

    Set<String> installedModules = splitInstallManager.getInstalledModules();
    

Cómo desinstalar módulos

Puedes pedirle al dispositivo que desinstale módulos. Para ello, invoca SplitInstallManager.deferredUninstall(List<String> moduleNames), como se muestra a continuación.

Kotlin

    // Specifies two dynamic feature modules for deferred uninstall.
    splitInstallManager.deferredUninstall(listOf("pictureMessages", "promotionalFilters"))
    

Java

    // Specifies two dynamic feature modules for deferred uninstall.
    splitInstallManager.deferredUninstall(Arrays.asList("pictureMessages", "promotionalFilters"));
    

Las desinstalaciones del módulo no se realizan inmediatamente. Es decir, el dispositivo los desinstala en el segundo plano según sea necesario para ahorrar espacio de almacenamiento. Para confirmar que el dispositivo haya quitado un módulo, invoca SplitInstallManager.getInstalledModules() e inspecciona el resultado, como se describe en la sección anterior.

Cómo descargar recursos de idiomas adicionales

Mediante Dynamic Delivery, los dispositivos solo descargan el código y los recursos necesarios para ejecutar tu app. Por lo tanto, el dispositivo de un usuario solo descarga los recursos de idiomas de tu app que coinciden con uno o más idiomas seleccionados actualmente en la configuración del dispositivo.

Si quieres que tu app tenga acceso a recursos de idiomas adicionales, por ejemplo, para implementar un selector de idiomas en la app, puedes usar la biblioteca de Play Core a fin de descargarlos a pedido. El proceso es similar al de descargar un módulo de funciones dinámicas, como se muestra a continuación.

Kotlin

    // Captures the user’s preferred language and persists it
    // through the app’s SharedPreferences.
    sharedPrefs.edit().putString(LANGUAGE_SELECTION, "fr").apply()
    ...

    // Creates a request to download and install additional language resources.
    val request = SplitInstallRequest.newBuilder()
            // Uses the addLanguage() method to include French language resources in the request.
            // Note that country codes are ignored. That is, if your app
            // includes resources for “fr-FR” and “fr-CA”, resources for both
            // country codes are downloaded when requesting resources for "fr".
            .addLanguage(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
            .build()

    // Submits the request to install the additional language resources.
    splitInstallManager.startInstall(request)
    

Java

    // Captures the user’s preferred language and persists it
    // through the app’s SharedPreferences.
    sharedPrefs.edit().putString(LANGUAGE_SELECTION, "fr").apply();
    ...

    // Creates a request to download and install additional language resources.
    SplitInstallRequest request =
        SplitInstallRequest.newBuilder()
            // Uses the addLanguage() method to include French language resources in the request.
            // Note that country codes are ignored. That is, if your app
            // includes resources for “fr-FR” and “fr-CA”, resources for both
            // country codes are downloaded when requesting resources for "fr".
            .addLanguage(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
            .build();

    // Submits the request to install the additional language resources.
    splitInstallManager.startInstall(request);
    

La solicitud se maneja como si fuese una solicitud de un módulo de funciones dinámicas. Es decir, puedes supervisar el estado de la solicitud como lo harías normalmente.

Si tu app no requiere los recursos de idiomas adicionales inmediatamente, puedes postergar la instalación de la app para cuando esté en el segundo plano, como se muestra a continuación.

Kotlin

    splitInstallManager.deferredLanguageInstall(
        Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
    

Java

    splitInstallManager.deferredLanguageInstall(
        Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)));
    

Cómo acceder a los recursos de idiomas descargados

Para obtener acceso inmediato a los recursos de idiomas descargados, tu app necesita ejecutar el método SplitCompat.installActivity() dentro del método attachBaseContext() de cada actividad que requiera acceso a esos recursos, como se muestra a continuación.

Kotlin

    override fun attachBaseContext(base: Context) {
      super.attachBaseContext(base)
      SplitCompat.installActivity(this)
    }
    

Java

    @Override
    protected void attachBaseContext(Context base) {
      super.attachBaseContext(base);
      SplitCompat.installActivity(this);
    }
    

Para cada actividad para la que quieres usar recursos de idiomas que descargó tu app, actualiza el contexto de la base y define una configuración regional nueva a través de su Configuration:

Kotlin

    override fun attachBaseContext(base: Context) {
      val configuration = Configuration()
      configuration.setLocale(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
      val context = base.createConfigurationContext(configuration)
      super.attachBaseContext(context)
      SplitCompat.install(this)
    }
    

Java

    @Override
    protected void attachBaseContext(Context base) {
      Configuration configuration = new Configuration();
      configuration.setLocale(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)));
      Context context = base.createConfigurationContext(configuration);
      super.attachBaseContext(context);
      SplitCompat.install(this);
    }
    

Para que estos cambios surtan efecto, tienes que volver a crear tu actividad después de que un idioma nuevo se instale y esté listo para usar. Puedes usar el método Activity#recreate().

Kotlin

    when (state.status()) {
      SplitInstallSessionStatus.INSTALLED -> {
          // Recreates the activity to load resources for the new language
          // preference.
          activity.recreate()
      }
      ...
    }
    

Java

    switch (state.status()) {
      case SplitInstallSessionStatus.INSTALLED:
          // Recreates the activity to load resources for the new language
          // preference.
          activity.recreate();
      ...
    }
    

Cómo desinstalar recursos de idiomas adicionales

Del mismo modo que los módulos de funciones dinámicas, puedes desinstalar recursos adicionales en cualquier momento. Antes de solicitar una desinstalación, determina qué idiomas están instalados actualmente, de la siguiente manera.

Kotlin

    val installedLanguages: Set<String> = splitInstallManager.installedLanguages
    

Java

    Set<String> installedLanguages = splitInstallManager.getInstalledLanguages();
    

Luego, puedes decidir qué idiomas desinstalar con el método deferredLanguageUninstall(), como se muestra a continuación.

Kotlin

    splitInstallManager.deferredLanguageUninstall(
        Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
    

Java

    splitInstallManager.deferredLanguageUninstall(
        Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)));
    

Recursos adicionales

Si deseas obtener más información sobre cómo usar la biblioteca de Play Core, prueba los siguientes recursos.

Muestras

Codelabs

  • Módulos a pedido, que sirven de ayuda para crear una app que descarga e instala funciones dinámicas a pedido.

Publicaciones de blog

Videos