Play Feature Delivery

Com o Play Feature Delivery, o app pode fazer o download de módulos de recursos sob demanda para os seguintes dispositivos:

  • Dispositivos móveis e tablets com o Android 5.0 (API de nível 21) e versões mais recentes
  • Dispositivos Chrome OS

Seu app só precisa chamar APIs na biblioteca Play Core 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 o módulo ao dispositivo. Você também pode usar essa API para fazer o download de módulos sob demanda para seus Instant Apps Android.

Se quiser aprender a adicionar módulos de recursos ao projeto e configurá-los para serem disponibilizados sob demanda, leia Criar um módulo de recursos.

Além disso, depois de ler este guia, veja a API em ação testando o app de amostra da API Play Core e aprenda a oferecer compatibilidade com atualizações no app.

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

Incluir a Play Core Library no seu projeto

Antes de começar, importe a biblioteca Play Core para o projeto.

Solicitar um módulo on demand

Quando seu app precisar usar um módulo de recurso, ele poderá solicitar um enquanto estiver em primeiro plano com a 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 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 ver mais informações, leia sobre os manifestos do módulo de recursos.

Por exemplo, imagine um app que tenha um módulo sob demanda para capturar e enviar mensagens gráficas usando a câmera do dispositivo, e esse módulo sob demanda 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 sob demanda, a biblioteca Play Core usa uma estratégia "disparar e esquecer". Isto é, ela 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 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 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 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.

Adiar a instalação de módulos sob demanda

Se você não precisar que o app faça o download de um módulo sob demanda 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 mais recente 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 uma 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 o download de pelo menos uma solicitação está sendo feito no momento. Verifique se há algum download de uma solicitação que ainda esteja sendo feito, 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 biblioteca Play Core não é compatível com o dispositivo atual. Ou seja, o dispositivo não pode fazer o download de recursos e instalá-los sob demanda. Para dispositivos com o Android 4.4 (nível da API 20) ou anterior, você precisa incluir módulos de recursos no momento da instalação usando a propriedade de manifesto dist:fusing. Para saber mais, leia sobre o manifesto do módulo de recursos.
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 sejam instalados 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.

Seu SplitInstallStateUpdatedListener recebe um SplitInstallSessionState com esse código de erro, status FAILED e ID da sessão -1.

INSUFFICIENT_STORAGE O dispositivo não tem espaço de armazenamento livre suficiente para instalar o módulo de recursos. Informe ao usuário que ele não tem espaço de armazenamento suficiente para instalar esse recurso.
APP_NOT_OWNED O app não foi instalado pelo Google Play, e o recurso não pode ser transferido por download. Na Play Core versão 1.9 ou mais recente, esse erro só pode ocorrer em instalações adiadas. Se você quiser que o usuário adquira o app no Google Play, use startInstall(), que pode receber a confirmação de usuário necessária (Play Core versão 1.9 ou mais recente).

Se um usuário solicitar o download de um módulo sob demanda 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 Help (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_DIED) {
       // 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. Geralmente, esse status ocorrerá se o app não tiver sido instalado pelo Google Play. Peça que o usuário confirme o download do recurso no Google Play. Para saber mais, vá para a seção sobre como receber a confirmação do usuário.
BAIXANDO O download está em andamento. Se você mostrar 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 a biblioteca SplitCompat para ter acesso a módulos transferidos por download e evitar esse estado. Isso é necessário para acessar o código e os recursos do módulo de recursos.
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 mais recentes, 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 o app não foi instalado pelo Google Play. 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. Seu app precisa pedir a confirmação ao usuário da seguinte forma:

Kotlin

override fun onSessionStateUpdate(state: SplitInstallSessionState) {
    if (state.status() == SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION) {
        // Displays a confirmation for the user to confirm 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 confirmation for the user to confirm 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 aceitar a confirmação, o status da solicitação mudará para PENDING e o download continuará.
  • Se o usuário negar a confirmação, o status da solicitação mudará para CANCELED.
  • Se o usuário não fizer uma seleção antes da caixa de diálogo ser destruída, o status da solicitação permanecerá como REQUIRES_USER_CONFIRMATION. Seu app pode pedir 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

Para acessar o código e os recursos de um módulo transferido por download, seu app precisa ativar a Biblioteca SplitCompat para seu app e cada atividade nos módulos de recursos que seu app transferir por download.

No entanto, a plataforma enfrenta as seguintes restrições para acessar o conteúdo de um módulo, por algum tempo (dias, em alguns casos) após o download do módulo:

  • 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 código e recursos de um módulo transferido por download, você precisa ativar a SplitCompat usando apenas um dos métodos descritos nas seções a seguir.

Depois de ativar a SplitCompat para seu app, você também precisa ativar a biblioteca SplitCompat para todas as atividades nos módulos de recurso aos quais você quer que seu app tenha acesso.

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 nos módulos de recursos transferidos por download 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. Ativar a SplitCompat dessa maneira é necessário para iniciar atividades incluídas em módulos de recursos. Para fazer isso, modifique 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 sob demanda 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 de base, você precisará ativá-la para todas as atividades que o app transferir para um módulo de recurso. 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 biblioteca SplitCompat para o contexto do seu app de base e as atividades no módulo dinâmico, após uma solicitação de um módulo sob demanda ser relatada 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 em 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 será atualizado. Como alternativa, você pode recriar esse componente ou instalar a SplitCompat após a instalação do módulo de recursos.

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 for relatado como INSTALLED, você poderá 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 tem essa informação, que pode ser acessada, 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 sob demanda para um Instant App Android no Android 8.0 (API de nível 26) e versões mais recentes, depois que uma solicitação de instalação for relatada como 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 estará 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 transferido para um Instant App, 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 estão instalados no dispositivo atualmente, você pode chamar SplitInstallManager.getInstalledModules(), que retorna um Set<String> dos nomes dos módulos instalados, como 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 feature modules for deferred uninstall.
splitInstallManager.deferredUninstall(listOf("pictureMessages", "promotionalFilters"))

Java

// Specifies two 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 faz a desinstalação 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

Com os pacotes de apps, os dispositivos fazem o download apenas do código e dos recursos necessários para executar o app. Assim, para recursos de idioma, o dispositivo de um usuário faz o download apenas dos recursos de idioma do 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 biblioteca Play Core para fazer o download sob demanda. O processo é semelhante ao de fazer o download de um módulo de recurso, 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 é processada como se fosse uma solicitação de um módulo de recurso. 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 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 que o app transferiu por download, atualize o contexto base e defina uma nova localidade com 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 estiver 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, é possível desinstalar outros recursos a qualquer momento. Antes de solicitar uma desinstalação, você pode determinar primeiro quais idiomas estão atualmente instalados, como mostrado 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 biblioteca Play Core, tente usar os recursos a seguir.

Amostras

Codelabs

  • Módulos sob demanda, que ajuda você a criar um app que faz o download de recursos sob demanda e os instala.

Postagens do blog

Vídeos