با ماژول های ویژگی پیمایش کنید

کتابخانه Dynamic Navigator عملکرد مولفه Jetpack Navigation را برای کار با مقاصدی که در ماژول های ویژگی تعریف شده اند گسترش می دهد. این کتابخانه همچنین نصب یکپارچه ماژول های ویژگی درخواستی را هنگام حرکت به این مقاصد فراهم می کند.

راه اندازی

برای پشتیبانی از ماژول های ویژگی، از وابستگی های زیر در فایل build.gradle ماژول برنامه خود استفاده کنید:

شیار

dependencies {
    def nav_version = "2.8.4"

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

کاتلین

dependencies {
    val nav_version = "2.8.4"

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

توجه داشته باشید که وابستگی‌های ناوبری دیگر باید از تنظیمات api استفاده کنند تا برای ماژول‌های ویژگی شما در دسترس باشند.

استفاده اساسی

برای پشتیبانی از ماژول های ویژگی، ابتدا همه نمونه های NavHostFragment در برنامه خود را به 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"
    ... />

سپس، یک ویژگی app:moduleName به هر مقصد <activity> ، <fragment> ، یا <navigation> در نمودارهای ناوبری ماژول com.android.dynamic-feature که با DynamicNavHostFragment مرتبط هستند، اضافه کنید. این ویژگی به کتابخانه Dynamic Navigator می گوید که مقصد متعلق به یک ماژول ویژگی با نامی است که شما مشخص می کنید.

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

هنگامی که به یکی از این مقاصد حرکت می کنید، کتابخانه Dynamic Navigator ابتدا بررسی می کند که آیا ماژول ویژگی نصب شده است یا خیر. اگر ماژول ویژگی از قبل وجود داشته باشد، برنامه شما همانطور که انتظار می رود به مقصد حرکت می کند. اگر ماژول موجود نباشد، برنامه شما هنگام نصب ماژول، مقصد قطعه پیشرفت متوسط ​​را نشان می دهد. اجرای پیش‌فرض قطعه پیشرفت یک رابط کاربری پایه با نوار پیشرفت را نشان می‌دهد و هرگونه خطای نصب را کنترل می‌کند.

دو صفحه بارگیری که UI را با نوار پیشرفت در هنگام پیمایش به ماژول ویژگی برای اولین بار نشان می دهد
شکل 1. UI که یک نوار پیشرفت را نشان می دهد، زمانی که کاربر برای اولین بار به یک ویژگی درخواستی پیمایش می کند. برنامه این صفحه را به عنوان دانلود ماژول مربوطه نمایش می دهد.

برای سفارشی کردن این رابط کاربری، یا برای کنترل دستی پیشرفت نصب از داخل صفحه برنامه خود، بخش سفارشی کردن پیشرفت و نظارت بر وضعیت درخواست را در این مبحث ببینید.

مقصدهایی که app:moduleName مشخص نمی کنند بدون تغییر به کار خود ادامه می دهند و طوری رفتار می کنند که گویی برنامه شما از NavHostFragment معمولی استفاده می کند.

قطعه پیشرفت را سفارشی کنید

می‌توانید اجرای قطعه پیشرفت را برای هر نمودار ناوبری با تنظیم app:progressDestination روی شناسه مقصدی که می‌خواهید برای مدیریت پیشرفت نصب استفاده کنید، لغو کنید. مقصد پیشرفت سفارشی شما باید یک Fragment باشد که از AbstractProgressFragment مشتق شده باشد. شما باید روش‌های انتزاعی را برای اعلان‌های مربوط به پیشرفت نصب، خطاها و سایر رویدادها لغو کنید. سپس می توانید پیشرفت نصب را در UI مورد نظر خود نشان دهید.

کلاس DefaultProgressFragment پیاده سازی پیش فرض از این API برای نشان دادن پیشرفت نصب استفاده می کند.

وضعیت درخواست را نظارت کنید

کتابخانه Dynamic Navigator شما را قادر می‌سازد تا یک جریان UX مشابه آنچه در بهترین شیوه‌های UX برای تحویل درخواستی وجود دارد ، پیاده‌سازی کنید، که در آن کاربر در حالی که منتظر پایان نصب است، در متن صفحه قبلی می‌ماند. این به این معنی است که شما اصلا نیازی به نمایش یک رابط کاربری متوسط ​​یا قطعه پیشرفت ندارید.

صفحه‌ای که نوار پیمایش پایینی را با نمادی نشان می‌دهد که نشان می‌دهد یک ماژول ویژگی در حال دانلود است
شکل 2. صفحه ای که پیشرفت دانلود را از نوار پیمایش پایین نشان می دهد.

در این سناریو شما مسئول نظارت و مدیریت تمامی حالت های نصب، تغییرات پیشرفت، خطاها و غیره هستید.

برای شروع این جریان ناوبری غیر مسدود کننده، یک شی DynamicExtras که حاوی DynamicInstallMonitor است را به NavController.navigate() ارسال کنید، همانطور که در مثال زیر نشان داده شده است:

کاتلین

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

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

جاوا

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

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

بلافاصله پس از فراخوانی navigate() باید مقدار installMonitor.isInstallRequired را بررسی کنید تا ببینید آیا تلاش برای ناوبری منجر به نصب ماژول ویژگی شده است یا خیر.

  • اگر مقدار false است، به یک مقصد عادی می روید و نیازی به انجام کار دیگری ندارید.
  • اگر مقدار true است، باید شروع به مشاهده شی LiveData کنید که اکنون در installMonitor.status است. این شی LiveData به‌روزرسانی‌های SplitInstallSessionState را از کتابخانه Play Core منتشر می‌کند. این به‌روزرسانی‌ها حاوی رویدادهای پیشرفت نصب هستند که می‌توانید از آنها برای به‌روزرسانی رابط کاربری استفاده کنید. به یاد داشته باشید که تمام وضعیت های مربوطه را همانطور که در راهنمای Play Core ذکر شده است، از جمله درخواست تأیید کاربر در صورت لزوم انجام دهید.

    کاتلین

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

    جاوا

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

هنگامی که نصب به پایان می رسد، شی LiveData وضعیت SplitInstallSessionStatus.INSTALLED را منتشر می کند. سپس باید NavController.navigate() را دوباره فراخوانی کنید. از آنجایی که ماژول اکنون نصب شده است، تماس اکنون با موفقیت انجام می شود و برنامه همانطور که انتظار می رود به مقصد حرکت می کند.

پس از رسیدن به یک حالت ترمینال، مانند زمانی که نصب کامل شد یا زمانی که نصب با شکست مواجه شد، برای جلوگیری از نشت حافظه، باید ناظر LiveData خود را حذف کنید. با استفاده از SplitInstallSessionStatus.hasTerminalStatus() می توانید بررسی کنید که آیا وضعیت وضعیت ترمینال را نشان می دهد.

AbstractProgressFragment را برای اجرای نمونه ای از این مشاهدهگر ببینید.

شامل نمودارها

کتابخانه Dynamic Navigator شامل نمودارهایی است که در ماژول های ویژگی تعریف شده اند. برای گنجاندن نموداری که در یک ماژول ویژگی تعریف شده است، موارد زیر را انجام دهید:

  1. همانطور که در مثال زیر نشان داده شده است، به جای <include <include/> <include-dynamic/> استفاده کنید:

    <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 ... /> ، باید ویژگی های زیر را مشخص کنید:

    • app:graphResName : نام فایل منبع گراف ناوبری. نام از نام فایل نمودار گرفته شده است. به عنوان مثال، اگر نمودار در res/navigation/nav_graph.xml باشد، نام منبع nav_graph است.
    • android:id - شناسه مقصد گراف. کتابخانه Dynamic Navigator هر مقدار android:id که در عنصر ریشه گراف موجود است نادیده می گیرد.
    • app:moduleName : نام بسته ماژول.

از graphPackage صحیح استفاده کنید

مهم است که app:graphPackage به درستی دریافت کنید، زیرا در غیر این صورت، مؤلفه Navigation قادر نخواهد بود navGraph مشخص شده را از ماژول ویژگی اضافه کند.

نام بسته یک ماژول ویژگی پویا با الحاق نام ماژول به applicationId ماژول برنامه پایه ساخته می شود. بنابراین اگر ماژول برنامه پایه دارای یک applicationId از com.example.dynamicfeatureapp باشد و ماژول ویژگی پویا DynamicFeatureModule نام داشته باشد، نام بسته ماژول پویا com.example.dynamicfeatureapp.DynamicFeatureModule خواهد بود. این نام بسته به حروف کوچک و بزرگ حساس است.

اگر شک دارید، می‌توانید نام بسته ماژول ویژگی را با بررسی AndroidManifest.xml ایجاد شده تأیید کنید. پس از ساخت پروژه به <DynamicFeatureModule>/build/intermediates/merged_manifest/debug/AndroidManifest.xml بروید، که باید چیزی شبیه به این باشد:

<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 باید با نام ماژول ویژگی پویا مطابقت داشته باشد و بسته با applicationId ماژول برنامه پایه مطابقت دارد. app:graphPackage ترکیبی از این موارد است: com.example.dynamicfeatureapp.DynamicFeatureModule .

فقط می توان به startDestination یک نمودار ناوبری include-dynamic پیمایش کرد. ماژول پویا مسئول نمودار ناوبری خود است و برنامه پایه هیچ اطلاعی از آن ندارد.

مکانیزم شامل پویا ماژول برنامه پایه را قادر می سازد که یک نمودار ناوبری تودرتو که در ماژول پویا تعریف شده است را شامل شود. این نمودار ناوبری تودرتو مانند هر نمودار ناوبری تو در تو عمل می کند. نمودار ناوبری ریشه (یعنی والد گراف تودرتو) فقط می تواند خود نمودار ناوبری تودرتو را به عنوان مقصد تعریف کند و نه فرزندان آن را. بنابراین، startDestination زمانی استفاده می شود که گراف ناوبری شامل-دینامیک مقصد باشد.

محدودیت ها

  • نمودارهایی که به صورت پویا درج شده اند در حال حاضر از پیوندهای عمیق پشتیبانی نمی کنند.
  • نمودارهای تو در تو با بارگذاری پویا (یعنی عنصر <navigation> با app:moduleName ) در حال حاضر از پیوندهای عمیق پشتیبانی نمی کنند.