Özellik modülleriyle gezinme

Dynamic Navigator kitaplığı, Jetpack Gezinme bileşeninin işlevlerini özellik modüllerinde tanımlanan hedeflerle çalışacak şekilde genişletir. Bu kitaplık, bu hedeflere gidildiğinde isteğe bağlı özellik modüllerinin sorunsuz bir şekilde yüklenmesini de sağlar.

Kurulum

Özellik modüllerini desteklemek için uygulama modülünüzün build.gradle dosyasında aşağıdaki bağımlılıkları kullanın:

Modern

dependencies {
    def nav_version = "2.7.7"

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

Kotlin

dependencies {
    val nav_version = "2.7.7"

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

Diğer Gezinme bağımlılıklarının özellik modüllerinizde kullanılabilmesi için api yapılandırmalarının kullanılması gerektiğini unutmayın.

Temel kullanım

Özellik modüllerini desteklemek için öncelikle uygulamanızdaki tüm NavHostFragment örneklerini androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment olarak değiştirin:

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

Ardından, com.android.dynamic-feature modülünüzün gezinme grafiklerinde DynamicNavHostFragment ile ilişkilendirilmiş tüm <activity>, <fragment> veya <navigation> hedeflerine app:moduleName özelliği ekleyin. Bu özellik, Dynamic Navigator kitaplığına hedefin belirttiğiniz ada sahip bir özellik modülüne ait olduğunu bildirir.

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

Bu hedeflerden birine gittiğinizde, Dynamic Navigator kitaplığı öncelikle özellik modülünün yüklü olup olmadığını kontrol eder. Özellik modülü zaten mevcutsa uygulamanız beklendiği gibi hedefe gider. Modül mevcut değilse uygulamanız, modülü yüklerken bir ara ilerleme parçası hedefi gösterir. İlerleme parçasının varsayılan uygulaması, ilerleme çubuğu olan temel bir kullanıcı arayüzü gösterir ve yükleme hatalarını yönetir.

bir özellik modülüne ilk kez gittiğinizde kullanıcı arayüzünde ilerleme çubuğu gösteren iki yükleme ekranı
Şekil 1. Kullanıcı isteğe bağlı bir özelliğe ilk kez geldiğinde ilerleme çubuğunu gösteren kullanıcı arayüzü. İlgili modül indirilirken uygulama bu ekranı görüntüler.

Bu kullanıcı arayüzünü özelleştirmek veya yükleme ilerleme durumunu kendi uygulama ekranınızdan manuel olarak yönetmek için bu konudaki İlerleme parçasını özelleştirme ve İstek durumunu izleme bölümlerine göz atın.

app:moduleName belirtilmeyen hedefler değişiklik olmadan çalışmaya devam eder ve uygulamanız normal bir NavHostFragment kullanıyormuş gibi davranır.

İlerleme parçasını özelleştirme

app:progressDestination özelliğini, yükleme ilerlemesini yönetmek için kullanmak istediğiniz hedefin kimliğine ayarlayarak her bir gezinme grafiğinin ilerleme parçası uygulamasını geçersiz kılabilirsiniz. Özel ilerleme hedefiniz, AbstractProgressFragment kaynağından gelen bir Fragment olmalıdır. Yükleme ilerlemesi, hatalar ve diğer etkinliklerle ilgili bildirimler için soyut yöntemleri geçersiz kılmanız gerekir. Daha sonra, yüklemenin ilerleme durumunu istediğiniz bir kullanıcı arayüzünde gösterebilirsiniz.

Varsayılan uygulamanın DefaultProgressFragment sınıfı, yüklemenin ilerleme durumunu göstermek için bu API'yi kullanır.

İstek durumunu izleme

Dynamic Navigator kitaplığı, isteğe bağlı sunum için kullanıcı deneyimi en iyi uygulamalarına benzer bir kullanıcı deneyimi akışı uygulamanıza olanak tanır. Bu akışta kullanıcı, kurulumun tamamlanmasını beklerken önceki bir ekranın bağlamında kalır. Bu, ara bir kullanıcı arayüzü veya ilerleme parçası göstermenize gerek olmadığı anlamına gelir.

bir özellik modülünün indirilmekte olduğunu belirten bir simgenin bulunduğu alt gezinme çubuğunun gösterildiği ekran
Şekil 2. İndirme işleminin ilerleme durumunu alt gezinme çubuğunda gösteren ekran.

Bu senaryoda tüm yükleme durumlarını, ilerleme durumu değişikliklerini, hataları vb. izlemek ve yönetmek sizin sorumluluğunuzdadır.

Engellemeyen bu gezinme akışını başlatmak için aşağıdaki örnekte gösterildiği gibi DynamicInstallMonitor içeren bir DynamicExtras nesnesini NavController.navigate() öğesine iletin:

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

navigate() çağırdıktan hemen sonra, gezinme denemesinin bir özellik modülü yüklemesiyle sonuçlanıp sonuçlanmadığını görmek için installMonitor.isInstallRequired değerini kontrol etmeniz gerekir.

  • Değer false ise normal bir hedefe gidersiniz ve başka bir şey yapmanız gerekmez.
  • Değer true ise şu anda installMonitor.status içinde olan LiveData nesnesini gözlemlemeye başlamanız gerekir. Bu LiveData nesnesi, Play Core kitaplığından SplitInstallSessionState güncellemeleri yayar. Bu güncellemeler, kullanıcı arayüzünü güncellemek için kullanabileceğiniz yükleme ilerleme durumu etkinliklerini içerir. Gerekirse kullanıcı onayı isteme de dahil olmak üzere tüm ilgili durumları Play Core kılavuzunda özetlendiği şekilde ele almayı unutmayın.

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

Yükleme tamamlandığında LiveData nesnesi SplitInstallSessionStatus.INSTALLED durumunu gösterir. Ardından NavController.navigate() tekrar aramalısınız. Modül yüklendiğinden çağrı başarılı olur ve uygulama beklendiği gibi hedefe gider.

Yükleme tamamlandığında veya yükleme başarısız olduğunda (örneğin, yükleme tamamlandığında veya yükleme başarısız olduğunda) bir terminal durumuna ulaştıktan sonra, bellek sızıntılarını önlemek için LiveData gözlemcinizi kaldırmanız gerekir. Durumun bir terminal durumunu temsil edip etmediğini kontrol etmek için SplitInstallSessionStatus.hasTerminalStatus() kullanabilirsiniz.

Bu gözlemcinin örnek uygulamasını görmek için AbstractProgressFragment adresine bakın.

Dahil edilen grafikler

Dynamic Navigator kitaplığı, özellik modüllerinde tanımlanan grafikleri dahil etmeyi destekler. Bir özellik modülünde tanımlanmış bir grafiği eklemek için şunları yapın:

  1. Aşağıdaki örnekte gösterildiği gibi <include/> yerine <include-dynamic/> kullanın:

    <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. <include-dynamic ... /> içinde şu özellikleri belirtmeniz gerekir:

    • app:graphResName: Gezinme grafiği kaynak dosyasının adı. Ad, grafiğin dosya adından türetilir. Örneğin, grafik res/navigation/nav_graph.xml içindeyse kaynak adı nav_graph olur.
    • android:id - grafik hedef kimliği. Dynamic Navigator kitaplığı, dahil edilen grafiğin kök öğesinde bulunan tüm android:id değerlerini yoksayar.
    • app:moduleName: modülün paket adı.

Doğru grafikPaketini kullanma

Gezinme bileşeni, belirtilen navGraph öğesini özellik modülünden ekleyemeyeceğinden app:graphPackage öğesinin doğru olması önemlidir.

Bir dinamik özellik modülünün paket adı, modülün adı temel uygulama modülünün applicationId bölümüne eklenerek oluşturulur. Dolayısıyla, temel uygulama modülünün applicationId değeri com.example.dynamicfeatureapp ise ve dinamik özellik modülünün adı DynamicFeatureModule ise dinamik modülün paket adı com.example.dynamicfeatureapp.DynamicFeatureModule olur. Bu paket adı büyük/küçük harfe duyarlıdır.

Herhangi bir şüpheniz varsa oluşturulan AndroidManifest.xml öğesini kontrol ederek özellik modülünün paket adını doğrulayabilirsiniz. Projeyi oluşturduktan sonra <DynamicFeatureModule>/build/intermediates/merged_manifest/debug/AndroidManifest.xml adresine gidin. URL aşağıdaki gibi görünmelidir:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:dist="http://schemas.android.com/apk/distribution"
    featureSplit="DynamicFeatureModule"
    package="com.example.dynamicfeatureapp"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="21"
        android:targetSdkVersion="30" />

    <dist:module
        dist:instant="false"
        dist:title="@string/title_dynamicfeaturemodule" >
        <dist:delivery>
            <dist:install-time />
        </dist:delivery>

        <dist:fusing dist:include="true" />
    </dist:module>

    <application />

</manifest>

featureSplit değeri dinamik özellik modülünün adıyla, paket ise temel uygulama modülünün applicationId değeriyle eşleşmelidir. app:graphPackage, şunların kombinasyonudur: com.example.dynamicfeatureapp.DynamicFeatureModule.

Bir include-dynamic gezinme grafiğinin startDestination bölümüne yalnızca gidilebilir. Dinamik modül kendi gezinme grafiğinden sorumludur ve temel uygulama bundan haberdar değildir.

include-dinamik mekanizması, temel uygulama modülünün dinamik modülde tanımlanan iç içe yerleştirilmiş bir gezinme grafiği içermesini sağlar. İç içe yerleştirilmiş bu gezinme grafiği, iç içe yerleştirilmiş gezinme grafikleri gibi davranır. Kök gezinme grafiği (yani iç içe yerleştirilmiş grafiğin üst öğesi), iç içe yerleştirilmiş gezinme grafiğinin kendisini yalnızca hedef olarak tanımlayabilir, bu grafiğin alt öğelerini tanımlayamaz. Dolayısıyla, dahil etme-dinamik gezinme grafiği hedef olduğunda startDestination kullanılır.

Sınırlamalar

  • Dinamik olarak eklenmiş grafikler şu anda derin bağlantıları desteklememektedir.
  • Dinamik olarak yüklenmiş iç içe yerleştirilmiş grafikler (yani, app:moduleName içeren bir <navigation> öğesi) şu anda derin bağlantıları desteklememektedir.