O Google tem o compromisso de promover a igualdade racial para as comunidades negras. Saiba como.

Navegar com módulos de recursos

A biblioteca Dynamic Navigator estende a funcionalidade do componente de navegação do Jetpack para funcionar com destinos definidos em módulos de recursos. Essa biblioteca também oferece a instalação simples de módulos de recursos sob demanda ao navegar até esses destinos.

Configurar

Para oferecer compatibilidade com módulos de recursos, use as seguintes dependências no arquivo build.gradle do módulo do app:

dependencies {
    def nav_version = "2.3.0"

    api "androidx.navigation:navigation-fragment-ktx:$nav_version"
    api "androidx.navigation:navigation-ui-ktx:$nav_version"
    api "androidx.navigation:navigation-dynamic-features-fragment:$nav_version"
}

Observe que as outras dependências de navegação precisam usar configurações de API para ficarem disponíveis aos módulos de recursos.

Uso básico

Para oferecer compatibilidade com módulos de recursos, primeiro mude todas as instâncias de NavHostFragment no seu app para androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment:

<androidx.fragment.app.FragmentContainerView
    android:id="@+id/nav_host_fragment"
    android:name="androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment"
    app:navGraph="@navigation/nav_graph"
    ... />

Em seguida, adicione um atributo app:moduleName a qualquer destino <activity>, <fragment> ou <navigation> nos gráficos de navegação associados a um DynamicNavHostFragment. Esse atributo informa à biblioteca Dynamic Navigator que o destino pertence a um módulo de recurso com o nome especificado por você.

<fragment
    app:moduleName="myDynamicFeature"
    android:id="@+id/featureFragment"
    android:name="com.google.android.samples.feature.FeatureFragment"
    ... />

Quando você navega para um desses destinos, a biblioteca Dynamic Navigator verifica primeiro se o módulo de recursos está instalado. Se o módulo de recursos já estiver presente, o app navegará para o destino conforme o esperado. Se o módulo não estiver presente, o app mostrará um destino de fragmento de progresso intermediário à medida que o módulo é instalado. A implementação padrão do fragmento de progresso mostra uma IU básica com uma barra de progresso e processa todos os erros de instalação.

duas telas de carregamento que mostram a IU com uma barra de progresso ao navegar
         em um módulo de recursos pela primeira vez.
Figura 1. IU mostrando uma barra de progresso quando um usuário navega para um recurso sob demanda pela primeira vez. O app exibirá essa tela durante o download do módulo correspondente.

Para personalizar essa IU ou para processar manualmente o progresso da instalação na tela do seu app, consulte as seções Personalizar o fragmento de progresso e Monitorar o estado da solicitação neste tópico.

Os destinos que não especificam app:moduleName continuam funcionando sem alterações e se comportam como se o app estivesse usando um NavHostFragment normal.

Personalizar o fragmento do progresso

É possível modificar a implementação do fragmento de progresso para cada gráfico de navegação definindo o atributo app:progressDestination como o ID do destino que você quer usar para processar o progresso da instalação. Seu destino de progresso personalizado precisa ser um Fragment derivado de AbstractProgressFragment. É preciso modificar os métodos abstratos para notificações sobre o progresso da instalação, erros e outros eventos. É possível mostrar o progresso da instalação em uma IU de sua escolha.

A classe DefaultProgressFragment da implementação padrão usa essa API para mostrar o progresso da instalação.

Monitorar o estado da solicitação

A biblioteca do Dynamic Navigator permite implementar um fluxo de UX semelhante ao das práticas recomendadas de UX para entrega sob demanda, em que o usuário mantém o contexto de uma tela anterior enquanto aguarda a conclusão da instalação. Isso significa que você não precisa exibir uma IU intermediária ou um fragmento de progresso.

tela que mostra uma barra de navegação inferior com um ícone que indica
         o download de um módulo de recursos.
Figura 2. Tela que mostra o progresso do download de uma barra de navegação na parte inferior.

Nesse cenário, você é responsável por monitorar e processar todos os estados de instalação, alterações de progresso, erros e assim por diante.

Para iniciar esse fluxo de navegação sem bloqueio, transmita um objeto DynamicExtras que contenha um DynamicInstallMonitor para NavController.navigate(), como mostrado no exemplo a seguir:

Kotlin

val navController = ...
val installMonitor = DynamicInstallMonitor()

navController.navigate(
    destinationId,
    null,
    null,
    DynamicExtras(installMonitor)
)

Java

NavController navController = ...
DynamicInstallMonitor installMonitor = new DynamicInstallMonitor();

navController.navigate(
    destinationId,
    null,
    null,
    new DynamicExtras(installMonitor);
)

Imediatamente após chamar navigate(), verifique o valor de installMonitor.isInstallRequired para ver se a tentativa de navegação resultou em uma instalação do módulo de recurso.

  • Se o valor for false, você estará navegando para um destino normal e não precisará fazer mais nada.
  • Se o valor for true, comece a observar o objeto LiveData que agora está em installMonitor.status. Esse objeto LiveData emite atualizações SplitInstallSessionState da biblioteca Play Core. Essas atualizações contêm eventos de progresso de instalação que podem ser usados para atualizar a IU. Lembre-se de processar todos os status relevantes conforme descrito no Guia da Play Core, incluindo a solicitação de confirmação do usuário se necessário.

    Kotlin

    val navController = ...
    val installMonitor = DynamicInstallMonitor()
    
    navController.navigate(
      destinationId,
      null,
      null,
      DynamicExtras(installMonitor)
    )
    
    if (installMonitor.isInstallRequired) {
      installMonitor.status.observe(this, object : Observer<SplitInstallSessionState> {
          override fun onChanged(sessionState: SplitInstallSessionState) {
              when (sessionState.status()) {
                  SplitInstallSessionStatus.INSTALLED -> {
                      // Call navigate again here or after user taps again in the UI:
                      // navController.navigate(destinationId, destinationArgs, null, null)
                  }
                  SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION -> {
                      SplitInstallManager.startConfirmationDialogForResult(...)
                  }
    
                  // Handle all remaining states:
                  SplitInstallSessionStatus.FAILED -> {}
                  SplitInstallSessionStatus.CANCELED -> {}
              }
    
              if (sessionState.hasTerminalStatus()) {
                  installMonitor.status.removeObserver(this);
              }
          }
      });
    }
    

    Java

    NavController navController = ...
    DynamicInstallMonitor installMonitor = new DynamicInstallMonitor();
    
    navController.navigate(
      destinationId,
      null,
      null,
      new DynamicExtras(installMonitor);
    )
    
    if (installMonitor.isInstallRequired()) {
      installMonitor.getStatus().observe(this, new Observer<SplitInstallSessionState>() {
          @Override
          public void onChanged(SplitInstallSessionState sessionState) {
              switch (sessionState.status()) {
                  case SplitInstallSessionStatus.INSTALLED:
                      // Call navigate again here or after user taps again in the UI:
                      // navController.navigate(mDestinationId, mDestinationArgs, null, null);
                      break;
                  case SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION:
                      SplitInstallManager.startConfirmationDialogForResult(...)
                      break;
    
                  // Handle all remaining states:
                  case SplitInstallSessionStatus.FAILED:
                      break;
                  case SplitInstallSessionStatus.CANCELED:
                      break;
              }
    
              if (sessionState.hasTerminalStatus()) {
                  installMonitor.getStatus().removeObserver(this);
              }
          }
      });
    }
    

Quando a instalação for concluída, o objeto LiveData emite um status SplitInstallSessionStatus.INSTALLED. Em seguida, chame NavController.navigate() novamente. Como agora o módulo está instalado, a chamada será bem-sucedida e o app navegará para o destino conforme o esperado.

Depois de alcançar um estado terminal, como quando a instalação é concluída ou quando a instalação falha, remova o observador LiveData para evitar vazamentos de memória. É possível verificar se o status representa um estado de terminal usando SplitInstallSessionStatus.hasTerminalStatus().

Consulte AbstractProgressFragment para ver um exemplo de implementação desse observador.

Gráficos incluídos

A biblioteca Dynamic Navigator é compatível com a inclusão de gráficos definidos em módulos de recursos. Para incluir um gráfico definido em um módulo de recursos, faça o seguinte:

  1. Use <include-dynamic/> em vez de <include/>, como mostrado no exemplo a seguir:

    <include-dynamic
        android:id="@+id/includedGraph"
        app:moduleName="includedgraphfeature"
        app:graphResName="included_feature_nav"
        app:graphPackage="com.google.android.samples.dynamic_navigator.included_graph_feature" />
    
  2. Dentro de <include-dynamic ... />, você precisa especificar os seguintes atributos:

    • app:graphResName: o nome do arquivo de recursos do gráfico de navegação. O nome é derivado do nome do arquivo do gráfico. Por exemplo, se o gráfico estiver em res/navigation/nav_graph.xml, o nome do recurso será nav_graph.
    • android:id: ID do gráfico de destino. A biblioteca Dynamic Navigator ignora todos os valores android:id encontrados no elemento raiz do gráfico incluído.
    • app:moduleName: nome do módulo de recursos.

Limitações

  • Os gráficos incluídos dinamicamente não são compatíveis com links diretos.
  • Gráficos aninhados carregados dinamicamente (ou seja, um elemento <navigation> com um app:moduleName) não são compatíveis com links diretos atualmente.

Compatibilidade com o Android Studio

Para usar o Navigation editor com a biblioteca Dynamic Navigator, você precisa usar a versão 4.0 ou mais recente do Android Studio.

Nas versões mais antigas do Android Studio, é preciso trabalhar diretamente com o XML do gráfico de navegação. Abrir um gráfico de navegação que contém qualquer uma das tags ou atributos descritos neste tópico pode causar exceções e outros comportamentos indefinidos.