ফিচার মডিউলের সাহায্যে নেভিগেট করা

ডায়নামিক নেভিগেটর লাইব্রেরি Jetpack নেভিগেশন কম্পোনেন্টের কার্যকারিতা বাড়ায় যাতে ফিচার মডিউলে সংজ্ঞায়িত গন্তব্যের সাথে কাজ করা যায়। এই লাইব্রেরি এইসব গন্তব্যে নেভিগেট করার সময় অন-ডিমান্ড ফিচার মডিউলের নিরবচ্ছিন্ন ইনস্টলেশনও প্রদান করে।

সেট-আপ করুন

ফিচার মডিউলকে সাপোর্ট করতে, আপনার অ্যাপ মডিউলের build.gradle ফাইলে নিম্নলিখিত ডিপেন্ডেন্সি ব্যবহার করুন:

গ্রুভি

dependencies {
    def nav_version = "2.10.2"

    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.10.2"

    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"
    ... />

এরপর, DynamicNavHostFragment-এর সাথে যুক্ত আপনার com.android.dynamic-feature মডিউলের নেভিগেশন গ্রাফে যেকোনও <activity>, <fragment> বা <navigation> ডেস্টিনেশনে একটি app:moduleName অ্যাট্রিবিউট যোগ করুন। এই অ্যাট্রিবিউট ডাইনামিক নেভিগেটর লাইব্রেরিকে জানায় যে গন্তব্যটি আপনার উল্লেখ করা নামের ফিচার মডিউলের অন্তর্গত।

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

আপনি এইসব গন্তব্যের কোনও একটিতে নেভিগেট করলে, ডায়নামিক নেভিগেটর লাইব্রেরি প্রথমে চেক করে দেখে যে ফিচার মডিউল ইনস্টল করা আছে কিনা। ফিচার মডিউল আগে থেকেই থাকলে, আপনার অ্যাপ প্রত্যাশা অনুযায়ী গন্তব্যে নেভিগেট করে। মডিউলটি না থাকলে, আপনার অ্যাপ মডিউলটি ইনস্টল করার সময় একটি ইন্টারমিডিয়েট প্রগ্রেস ফ্র্যাগমেন্ট ডেস্টিনেশন দেখায়। প্রোগ্রেস ফ্র্যাগমেন্টের ডিফল্ট ইমপ্লিমেন্টেশন প্রোগ্রেস বার সহ একটি বেসিক UI দেখায় এবং যেকোনও ইনস্টলেশন সংক্রান্ত সমস্যা ম্যানেজ করে।

দুটি লোডিং স্ক্রিন যা প্রথমবার ফিচার মডিউলে নেভিগেট করার সময় প্রোগ্রেস বারের সাথে UI দেখায়
ছবি ১. ব্যবহারকারী প্রথমবার অন-ডিমান্ড ফিচারে নেভিগেট করলে UI-তে প্রোগ্রেস বার দেখানো হয়। সংশ্লিষ্ট মডিউল ডাউনলোড হওয়ার সময় অ্যাপ এই স্ক্রিন দেখায়।

এই UI কাস্টমাইজ করতে অথবা আপনার নিজের অ্যাপ স্ক্রিন থেকে ইনস্টলেশনের প্রগ্রেস ম্যানুয়ালি ম্যানেজ করতে, এই বিষয়ের প্রগ্রেস ফ্র্যাগমেন্ট কাস্টমাইজ করুন এবং অনুরোধের স্ট্যাটাস মনিটর করুন বিভাগ দেখুন।

যেসব ডেস্টিনেশন app:moduleName উল্লেখ করে না, সেগুলি কোনও পরিবর্তন ছাড়াই কাজ করা চালিয়ে যায় এবং এমনভাবে আচরণ করে যেন আপনার অ্যাপ রেগুলার NavHostFragment ব্যবহার করে।

প্রোগ্রেস ফ্র্যাগমেন্ট কাস্টমাইজ করুন

আপনি প্রতিটি নেভিগেশন গ্রাফের জন্য প্রগ্রেস ফ্র্যাগমেন্ট ইমপ্লিমেন্টেশন ওভাররাইড করতে পারেন app:progressDestination অ্যাট্রিবিউটকে ডেস্টিনেশনের আইডিতে সেট করে যা আপনি ইনস্টলেশন প্রগ্রেস ম্যানেজ করার জন্য ব্যবহার করতে চান। আপনার কাস্টম প্রগ্রেস ডেস্টিনেশন একটি Fragment হতে হবে যা AbstractProgressFragment থেকে পাওয়া যায়। ইনস্টলেশন প্রগ্রেস, ত্রুটি এবং অন্যান্য ইভেন্ট সম্পর্কে বিজ্ঞপ্তির জন্য আপনাকে অবশ্যই অ্যাবস্ট্রাক্ট পদ্ধতি ওভাররাইড করতে হবে। তারপর আপনি নিজের পছন্দের UI-তে ইনস্টলেশনের প্রোগ্রেস দেখাতে পারবেন।

ডিফল্ট ইমপ্লিমেন্টেশনের DefaultProgressFragment ক্লাস ইনস্টলেশনের প্রোগ্রেস দেখানোর জন্য এই API ব্যবহার করে।

অনুরোধের স্ট্যাটাস মনিটর করা

ডাইনামিক নেভিগেটর লাইব্রেরি আপনাকে অন-ডিমান্ড ডেলিভারির জন্য UX সংক্রান্ত পেশাদার পদ্ধতি-তে উল্লেখ করা UX ফ্লোয়ের মতো ফ্লো প্রয়োগ করতে দেয়, যেখানে ইনস্টলেশন সম্পূর্ণ হওয়ার জন্য অপেক্ষা করার সময় ব্যবহারকারী আগের স্ক্রিনের প্রসঙ্গে থাকেন। এর অর্থ হল, আপনাকে কোনও ইন্টারমিডিয়েট UI বা প্রোগ্রেস ফ্র্যাগমেন্ট দেখাতে হবে না।

স্ক্রিন যাতে নিচের নেভিগেশন বারে একটি আইকন দেখাচ্ছে যা ইঙ্গিত করে
         যে একটি ফিচার মডিউল ডাউনলোড হচ্ছে
ছবি ২. স্ক্রিন যা নিচের নেভিগেশন বার থেকে ডাউনলোডের প্রগ্রেস দেখায়।

এই পরিস্থিতিতে, আপনাকে সব ইনস্টলেশন স্ট্যাটাস, প্রগ্রেস পরিবর্তন, সমস্যা এবং আরও অনেক কিছু মনিটর ও ম্যানেজ করতে হবে।

এই নন-ব্লকিং নেভিগেশন ফ্লো শুরু করতে, একটি DynamicExtras অবজেক্ট পাস করুন যাতে একটি DynamicInstallMonitor থেকে NavController.navigate() পর্যন্ত থাকে, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে:

Kotlin

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 অবজেক্ট Play Core লাইব্রেরি থেকে SplitInstallSessionState আপডেট এমিট করে। এইসব আপডেটে ইনস্টলেশন প্রগ্রেস ইভেন্ট থাকে যা আপনি UI আপডেট করতে ব্যবহার করতে পারেন। Play Core গাইড-এ উল্লেখ করা সমস্ত প্রাসঙ্গিক স্ট্যাটাস ম্যানেজ করতে ভুলবেন না, যার মধ্যে প্রয়োজন হলে ব্যবহারকারীর কনফার্মেশন চাওয়া অন্তর্ভুক্ত।

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

    জাভা

    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 দেখুন।

অন্তর্ভুক্ত গ্রাফ

ফিচার মডিউলে সংজ্ঞায়িত গ্রাফ অন্তর্ভুক্ত করার ক্ষেত্রে ডায়নামিক নেভিগেটর লাইব্রেরি কাজ করে। ফিচার মডিউলে সংজ্ঞায়িত করা গ্রাফ অন্তর্ভুক্ত করতে, নিম্নলিখিত ধাপগুলি অনুসরণ করুন:

  1. <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 সঠিকভাবে পাওয়া গুরুত্বপূর্ণ কারণ তা না হলে নেভিগেশন কম্পোনেন্ট ফিচার মডিউল থেকে নির্দিষ্ট করা 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।

শুধুমাত্র include-dynamic নেভিগেশন গ্রাফের startDestination-এ নেভিগেট করা সম্ভব। ডায়নামিক মডিউল তার নিজস্ব নেভিগেশন গ্রাফের জন্য দায়ী এবং বেস অ্যাপের কাছে সেই সম্পর্কে কোনও তথ্য থাকে না।

include-dynamic মেকানিজম বেস অ্যাপ মডিউলকে ডায়নামিক মডিউলের মধ্যে সংজ্ঞায়িত নেস্টেড নেভিগেশন গ্রাফ অন্তর্ভুক্ত করার সুবিধা দেয়। এই নেস্টেড নেভিগেশন গ্রাফ যেকোনও নেস্টেড নেভিগেশন গ্রাফের মতো কাজ করে। রুট নেভিগেশন গ্রাফ (অর্থাৎ, নেস্টেড গ্রাফের পেরেন্ট) শুধুমাত্র নেস্টেড নেভিগেশন গ্রাফকে নিজেই একটি গন্তব্য হিসেবে সংজ্ঞায়িত করতে পারে, তার চাইল্ডকে নয়। তাই, startDestination ব্যবহার করা হয় যখন include-dynamicnavigation গ্রাফটি গন্তব্য হয়।

সীমাবদ্ধতা

  • ডায়নামিক্যালি-ইনক্লুডেড গ্রাফে বর্তমানে ডিপ লিঙ্ক কাজ করে না।
  • ডায়নামিক্যালি লোড করা নেস্টেড গ্রাফে (অর্থাৎ, app:moduleName সহ <navigation> এলিমেন্ট) বর্তমানে ডিপ লিঙ্ক কাজ করে না।