Participe do evento ⁠#Android11: apresentação de lançamento da versão Beta no dia 3 de junho.

Download de módulos com a Play Core Library

Com o Dynamic Delivery do Google Play, seu app pode fazer o download de módulos de recursos dinâmicos on demand para dispositivos com Android 5.0 (API de nível 21) ou versões posteriores. Seu app só precisa chamar APIs na Play Core Library para fazer o download desses módulos e instalá-los, conforme necessário. A Google Play Store envia apenas o código e os recursos necessários para esse módulo ao dispositivo. Você também pode usar essa API para fazer o download de módulos on demand para seus Instant Apps Android.

Para saber como primeiro adicionar módulos de recursos dinâmicos ao projeto e configurá-los para que sejam disponibilizados on demand, leia Criar um módulo de recurso dinâmico.

Além disso, depois de ler este guia, veja a API em ação testando o app de amostra da API principal do Google Play e saiba como oferecer suporte para atualizações no app.

Por fim, antes de publicar o app, teste seu pacote de apps para verificar se a funcionalidade on demand do seu app funciona conforme o esperado.

Incluir a Play Core Library no seu projeto

Antes de começar a usar a Play Core Library, você precisa primeiro importá-la para o módulo do app como uma dependência do Gradle, conforme mostrado abaixo.

    // 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'
        ...
    }
    

Solicitar um módulo on demand

Quando seu app precisa usar um módulo de recurso dinâmico, ele pode solicitar um módulo enquanto está em primeiro plano por meio da classe SplitInstallManager. Ao fazer uma solicitação, seu app precisa especificar o nome do módulo, conforme definido pelo elemento split no manifesto do módulo de destino. Quando você cria um módulo de recurso dinâmico usando o Android Studio, o sistema de compilação usa o nome do módulo fornecido para injetar essa propriedade no manifesto do módulo no tempo de compilação. Para saber mais, leia sobre os Manifestos do módulo de recursos dinâmicos.

Por exemplo, imagine um app que tenha um módulo on demand para capturar e enviar mensagens gráficas usando a câmera do dispositivo, e esse módulo on demand especifica split="pictureMessages" no manifesto. O exemplo a seguir usa SplitInstallManager para solicitar o módulo pictureMessages (com um módulo adicional para alguns filtros promocionais):

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

Quando seu app solicita um módulo on demand, a Play Core Library usa uma estratégia "disparar e esquecer". Isto é, ele envia a solicitação para fazer o download do módulo para a plataforma, mas não monitora se a instalação foi bem-sucedida. Para avançar a jornada do usuário após a instalação ou manipular adequadamente os erros, monitore o estado da solicitação.

Observação: não há problema em solicitar um módulo de recurso dinâmico que já esteja instalado no dispositivo. A API considerará instantaneamente que a solicitação foi concluída se detectar que o módulo já está instalado. Além disso, depois que um módulo é instalado, o Google Play o mantém atualizado automaticamente. Ou seja, quando você faz o upload de uma nova versão do seu pacote de apps, a plataforma atualiza todos os APKs instalados que pertencem ao seu app. Para saber mais, leia Gerenciar atualizações do app.

Para ter acesso imediato ao código e aos recursos do módulo, seu app precisa ativar a biblioteca SplitCompat. A SplitCompat não é necessária para os Instant Apps Android. Eles sempre têm acesso imediato aos módulos de recursos.

Adiar a instalação de módulos on demand

Se você não precisar que o app faça o download de um módulo on demand e o instale imediatamente, poderá adiar a instalação para quando o app estiver em segundo plano. Um exemplo disso é se você quiser pré-carregar algum material promocional para uma versão posterior do app.

Você pode especificar um módulo para download posterior usando o método deferredInstall(), conforme mostrado abaixo. E, diferentemente de SplitInstallManager.startInstall(), seu app não precisa estar em primeiro plano para iniciar uma solicitação de instalação adiada.

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

As solicitações de instalações adiadas são mais eficazes, e não é possível acompanhar o progresso delas. Portanto, antes de tentar acessar um módulo especificado para a instalação adiada, você precisa verificar se o módulo foi instalado. Se você precisar que o módulo esteja disponível imediatamente, use SplitInstallManager.startInstall() para solicitá-lo, conforme mostrado na seção anterior.

Monitorar o estado da solicitação

Para atualizar uma barra de progresso, acionar um intent após a instalação ou manipular adequadamente um erro de solicitação, você precisa detectar as atualizações de estado da tarefa SplitInstallManager.startInstall() assíncrona. Antes de começar a receber atualizações para sua solicitação de instalação, registre um listener e acesse o ID da sessão para a solicitação, conforme mostrado abaixo.

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

Gerenciar erros de solicitação

Você precisa lidar com falhas ao fazer o download ou instalar um módulo usando addOnFailureListener(), conforme mostrado abaixo.

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

A tabela abaixo descreve os estados de erro que seu app pode precisar gerenciar.

Código do erro Descrição Ação sugerida
ACTIVE_SESSIONS_LIMIT_EXCEEDED A solicitação foi rejeitada porque pelo menos uma solicitação está sendo baixada no momento. Verifique se há alguma solicitação que ainda esteja sendo transferida, conforme mostrado no exemplo acima.
MODULE_UNAVAILABLE O Google Play não consegue encontrar o módulo solicitado com base na versão atual instalada do app, dispositivo e conta do Google Play do usuário. Se o usuário não tiver acesso ao módulo, notifique-o.
INVALID_REQUEST O Google Play recebeu a solicitação, mas ela não é válida. Verifique se as informações incluídas na solicitação estão completas e precisas.
SESSION_NOT_FOUND Uma sessão para um determinado ID de sessão não foi encontrada. Se você estiver tentando monitorar o estado de uma solicitação pelo ID da sessão, verifique se o ID da sessão está correto.
API_NOT_AVAILABLE A Play Core Library não é compatível com o dispositivo atual. Ou seja, o dispositivo não pode fazer o download de recursos on demand e instalá-los. Para dispositivos que executam o Android 4.4 (API de nível 20) ou versões anteriores, você precisa incluir módulos de recursos dinâmicos no momento da instalação usando a propriedade de manifesto dist:fusing. Para saber mais, leia sobre o Manifesto do módulo de recursos dinâmicos.
ACCESS_DENIED O app não pode registrar a solicitação devido a permissões insuficientes. Isso normalmente ocorre quando o app está em segundo plano. Tentativa da solicitação quando o app retorna para o primeiro plano.
NETWORK_ERROR A solicitação falhou devido a um erro de rede. Peça para que o usuário estabeleça uma conexão de rede ou mude para uma rede diferente.
INCOMPATIBLE_WITH_EXISTING_SESSION A solicitação contém um ou mais módulos que já foram solicitados, mas ainda não foram instalados. Crie uma nova solicitação que não inclua módulos já solicitados pelo seu app ou aguarde até que todos os módulos solicitados no momento concluam a instalação, antes de tentar novamente a solicitação.

Lembre-se de que a solicitação de um módulo que já foi instalado não é resolvida em um erro.

SERVICE_DIED O serviço responsável pelo processamento da solicitação foi eliminado. Tente fazer a solicitação novamente.

Esse código de erro é exposto como uma atualização para SplitInstallStateUpdatedListener com status FAILED e ID de sessão -1.

Se um usuário solicitar o download de um módulo on demand e ocorrer um erro, considere a exibição de uma caixa de diálogo que fornece duas opções para o usuário: Try again (que tenta a solicitação novamente) e Cancel (que abandona a solicitação). Para oferecer suporte adicional, você também precisa fornecer o link Ajuda que direciona os usuários para a Central de Ajuda do Google Play.

Gerenciar atualizações de estado

Depois de registrar um listener e registrar o ID da sessão para sua solicitação, use StateUpdatedListener.onStateUpdate() para gerenciar alterações de estado, conforme mostrado abaixo.

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

Os estados possíveis para sua solicitação de instalação estão descritos na tabela abaixo.

Estado de solicitação Descrição Ação sugerida
PENDING A solicitação foi aceita, e o download deve começar em breve. Inicialize componentes da IU, por exemplo, uma barra de progresso, para fornecer feedback ao usuário sobre o download.
REQUIRES_USER_CONFIRMATION O download requer confirmação do usuário. Provavelmente, isso ocorre porque o tamanho do download é maior que 10 MB. Peça que o usuário aceite a solicitação de download. Para saber mais, vá para a seção sobre como receber a confirmação do usuário.
DOWNLOADING O download está em andamento. Se você mostra uma barra de progresso para o download, use os métodos SplitInstallSessionState.bytesDownloaded() e SplitInstallSessionState.totalBytesToDownload() para atualizar a IU (consulte a amostra de código acima desta tabela).
DOWNLOADED O dispositivo fez o download do módulo, mas a instalação ainda não começou. Os apps precisam ativar SplitCompat para ter acesso imediato aos módulos baixados e evitar esse estado. Caso contrário, o download será transferido para INSTALLED, e seu app acessará o código e os recursos dele somente em algum momento após o app entrar em segundo plano.
INSTALLING O dispositivo está instalando o módulo no momento. Atualize a barra de progresso. Esse estado normalmente é curto.
INSTALLED O módulo está instalado no dispositivo. Acesse o código e o recurso no módulo para continuar a jornada do usuário.

Se o módulo for para um Instant App Android executado no Android 8.0 (API de nível 26) ou versões posteriores, você precisará usar splitInstallHelper para atualizar componentes do app com o novo módulo.

FAILED A solicitação falhou antes da instalação do módulo no dispositivo. Peça que o usuário tente fazer a solicitação novamente ou cancelá-la.
CANCELING O dispositivo está cancelando a solicitação. Para saber mais, vá para a seção sobre como cancelar uma solicitação de instalação.
CANCELED A solicitação foi cancelada.

Receber a confirmação do usuário

Em alguns casos, o Google Play pode exigir a confirmação do usuário antes de atender a uma solicitação de download. Por exemplo, se uma solicitação exigir um download grande e o dispositivo estiver usando dados móveis. Nesses casos, o status da solicitação informa REQUIRES_USER_CONFIRMATION, e seu app precisa receber a confirmação do usuário antes que o dispositivo possa fazer o download dos módulos na solicitação e instalá-los. Para receber a confirmação, seu app precisa pedir ao usuário da seguinte forma:

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

O status da solicitação é atualizado dependendo da resposta do usuário:

  • Se o usuário selecionar “Download”, o status da solicitação mudará para PENDING, e o download continuará.
  • Se o usuário selecionar "Cancelar", o status da solicitação será alterado para CANCELED.
  • Se o usuário não fizer uma seleção antes de a caixa de diálogo ser destruída, o status da solicitação permanecerá como REQUIRES_USER_CONFIRMATION. Seu app pode solicitar novamente que o usuário conclua a solicitação.
  • Para receber um callback com a resposta do usuário, use onActivityResult(), conforme mostrado abaixo.

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

    Cancelar uma solicitação de instalação

    Se o app precisar cancelar uma solicitação antes de ser instalado, poderá invocar o método cancelInstall() usando o ID de sessão da solicitação, conforme mostrado abaixo.

    Kotlin

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

    Java

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

    Acessar módulos imediatamente

    Para acessar imediatamente o código e os recursos de um módulo transferido, ou seja, antes da reinicialização do app, seu app precisa ativar a Biblioteca SplitCompat tanto para seu app e como para cada atividade nos módulos de recursos dinâmicos baixados.

    No entanto, a plataforma enfrenta as seguintes restrições para acessar o conteúdo de um módulo antes da reinicialização do app:

  • A plataforma não pode aplicar novas entradas de manifesto introduzidas pelo módulo.
  • A plataforma não pode acessar os recursos do módulo para componentes da IU do sistema, por exemplo, as notificações. Se você precisar usar esses recursos imediatamente, inclua-os no módulo básico do seu app.
  • Ativar a SplitCompat

    Para que seu app acesse imediatamente o código e os recursos de um módulo baixado, você precisa ativar a SplitCompat usando apenas um dos métodos descritos abaixo.

    Depois de ativar SplitCompat para seu app, você também precisa ativá-la para cada atividade nos módulos de recurso dinâmico a que o app terá acesso imediato.

    Declarar SplitCompatApplication no manifesto

    A maneira mais simples de ativar a SplitCompat é declarar SplitCompatApplication como a subclasse Application no manifesto do seu app, conforme mostrado abaixo.

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

    Depois que o app for instalado em um dispositivo, você poderá acessar o código e os recursos a partir dos módulos de recursos dinâmicos baixados automaticamente.

    Invocar a SplitCompat no momento da execução

    Você também pode ativar a SplitCompat em atividades ou serviços específicos no momento da execução. A ativação da SplitCompat dessa forma é necessária para iniciar as atividades incluídas nos módulos do recurso imediatamente após a instalação do módulo. Para fazer isso, substitua attachBaseContext, conforme mostrado abaixo.

    Se você tiver uma classe Application personalizada, faça com que SplitCompatApplication seja estendida para ativar a SplitCompat para seu app, conforme mostrado abaixo.

    Kotlin

        class MyApplication : SplitCompatApplication() {
            ...
        }
        

    Java

        public class MyApplication extends SplitCompatApplication {
            ...
        }
        

    SplitCompatApplication simplesmente modifica ContextWrapper.attachBaseContext() para incluir SplitCompat.install(Context applicationContext). Se você não quiser que a classe Application estenda SplitCompatApplication, modifique o método attachBaseContext() manualmente, da seguinte maneira:

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

    Caso seu módulo on demand seja compatível com apps instantâneos e apps instalados, você poderá invocar condicionalmente a SplitCompat da seguinte maneira:

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

    Ativar a SplitCompat para atividades do módulo

    Depois de ativar a SplitCompat para seu app básico, você precisará ativá-la para cada atividade de que o app fizer download em um módulo de recurso dinâmico. Para fazer isso, use o método SplitCompat.installActivity() da seguinte maneira:

    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ódigo de acesso e recursos dos módulos instalados

    Contanto que você ative a SplitCompat para o contexto do seu app base e as atividades no módulo dinâmico, após uma solicitação de um módulo on demand ser relatado como INSTALLED, você poderá começar a usar o código e os recursos como se fizessem parte do APK de base.

    Se quiser acessar recursos existentes no módulo recém-instalado a partir de um módulo instalado diferente do seu app, você precisará fazer isso usando o contexto do aplicativo. O contexto do componente que está tentando acessar os recursos ainda não foi atualizado. Como alternativa, você pode recriar esse componente ou instalar a SplitCompat após a instalação do módulo de recurso dinâmico.

    Além disso, não armazene em cache objetos ApplicationInfo do Android, seus conteúdos ou objetos que os contenham no seu app. Você sempre tem que buscar esses objetos conforme necessário a partir de um contexto de app. Armazenar esses objetos em cache pode fazer com que o app falhe ao instalar uma divisão em versões mais recentes do Android.

    Acessar Instant Apps Android instalados

    Depois que um módulo do Instant App Android é chamado de INSTALLED, você pode acessar o código e os recursos dele usando um contexto de app atualizado. Um contexto que seu app cria antes de instalar um módulo (por exemplo, um que já esteja armazenado em uma variável) não possui o conteúdo do novo módulo. Mas um novo contexto faz isso. Ele pode ser acessado, por exemplo, usando 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();
                }
            }
        }
        

    Instant Apps Android no Android 8.0 e versões posteriores

    Ao solicitar um módulo on demand para um Instant App Android no Android 8.0 (API de nível 26) e versões posteriores, depois que uma solicitação de instalação for chamada de INSTALLED, você precisará atualizar o app com o contexto do novo módulo por meio de uma chamada para SplitInstallHelper.updateAppInfo(Context context). Caso contrário, o app ainda não está ciente do código e dos recursos do módulo. Depois de atualizar os metadados do app, você precisa carregar o conteúdo do módulo durante o próximo evento da linha de execução principal invocando um novo Handler, conforme mostrado abaixo.

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

    Carregar bibliotecas C/C++

    Se você quiser carregar bibliotecas C/C++ de um módulo que o dispositivo já tenha baixado, use SplitInstallHelper.loadLibrary(Context context, String libName), conforme mostrado abaixo.

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

    Gerenciar módulos instalados

    Para verificar quais módulos de recursos dinâmicos estão atualmente instalados no dispositivo, você pode chamar SplitInstallManager.getInstalledModules(), que retorna um Set<String> dos nomes dos módulos instalados, conforme mostrado abaixo.

    Kotlin

        val installedModules: Set<String> = splitInstallManager.installedModules
        

    Java

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

    Desinstalar módulos

    Você pode solicitar que o dispositivo desinstale módulos invocando SplitInstallManager.deferredUninstall(List<String> moduleNames), conforme mostrado abaixo.

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

    As desinstalações do módulo não ocorrem imediatamente. Ou seja, o dispositivo as desinstala em segundo plano, conforme necessário, para economizar espaço de armazenamento. Você pode confirmar se o dispositivo excluiu um módulo invocando SplitInstallManager.getInstalledModules() e inspecionando o resultado, conforme descrito na seção anterior.

    Fazer o download de outros recursos de idioma

    Por meio do Dynamic Delivery, os dispositivos fazem o download somente do código e dos recursos necessários para executar seu app. Assim, para recursos de idioma, o dispositivo de um usuário faz o download apenas dos recursos de idioma do seu app que correspondem a um ou mais idiomas selecionados atualmente nas configurações do dispositivo.

    Se você quiser que seu app tenha acesso a outros recursos de idioma, por exemplo, para implementar um seletor de idioma no app, use a Play Core Library para fazer o download on demand. O processo é semelhante ao de fazer o download de um módulo de recurso dinâmico, conforme mostrado abaixo.

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

    A solicitação é tratada como se fosse uma solicitação de um módulo de recurso dinâmico. Ou seja, você pode monitorar o estado da solicitação como faria normalmente.

    Caso seu app não exija os recursos de idioma adicionais imediatamente, você poderá adiar a instalação para quando o app estiver em segundo plano, conforme mostrado abaixo.

    Kotlin

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

    Java

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

    Acessar recursos de idiomas transferidos por download

    Para ter acesso imediato aos recursos de idioma transferidos por download, seu app precisa executar o método SplitCompat.installActivity() no método attachBaseContext() de cada atividade que requer acesso a esses recursos, conforme mostrado abaixo.

    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 atividade em que você quer usar os recursos de idioma baixados pelo seu app, atualize o contexto base e defina uma nova localidade por meio de 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 essas mudanças entrem em vigor, é necessário recriar sua atividade depois que o novo idioma está instalado e pronto para uso. Você pode usar o 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();
          ...
        }
        

    Desinstalar outros recursos de idioma

    Assim como os módulos de recursos dinâmicos, você pode desinstalar outros recursos a qualquer momento. Antes de solicitar uma desinstalação, você pode determinar primeiro quais idiomas estão atualmente instalados, como a seguir.

    Kotlin

        val installedLanguages: Set<String> = splitInstallManager.installedLanguages
        

    Java

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

    Depois, você pode decidir quais idiomas serão desinstalados usando o método deferredLanguageUninstall(), conforme mostrado abaixo.

    Kotlin

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

    Java

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

    Outros recursos

    Para saber mais sobre como usar a Play Core Library, tente usar os recursos a seguir.

    Amostras

  • Amostra da API PlayCore (link em inglês), que demonstra o uso da API PlayCore para solicitar recursos dinâmicos e fazer o download deles.
  • Amostra de carregamento de código dinâmico (link em inglês), que demonstra três abordagens diferentes para acessar o código com segurança a partir de um módulo de recursos dinâmicos instalado.
  • Codelabs

  • Módulos sob demanda (link em inglês), que ajudam a criar um app que faz o download de recursos dinâmicos sob demanda e os instala.
  • Postagens do blog

  • Novos recursos para desenvolver, lançar e expandir seus negócios no Google Play
  • As atualizações mais recentes do Android App Bundle, incluindo a API de outros idiomas
  • Vídeos

  • Entrega personalizável com o Android App Bundle e o compartilhamento fácil de compilações de teste (em inglês)
  • Novas ferramentas para otimizar o tamanho do seu app e aumentar as instalações no Google Play (em inglês)