Search code examples
androiddynamic-feature-moduledynamic-featureandroid-app-bundle

App is not able to find DynamicFeature navigation graph. Getting Resources$NotFoundException for the navigation graph


I am using Android's Dynamic delivery for one of my feature. I have separated the code for the feature. I am also using the Navigation component in my project.

I can see dynamicfeature being downloaded from the progress bar and after downloading I am using Navigation component to navigate to Fragment2.

However, when I am trying to navigate from Fragment1 which is in my "app" to Fragment2 which is in my "dynamicfeature" I am getting below exception.

Fatal Exception: android.content.res.Resources$NotFoundException: com.sample.sample.debug.dynamicfeature:navigation/dynamic_feature_nav
   at androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.replaceWithIncludedNav(DynamicIncludeGraphNavigator.kt:95)
   at androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.navigate(DynamicIncludeGraphNavigator.kt:79)
   at androidx.navigation.dynamicfeatures.DynamicIncludeGraphNavigator.navigate(DynamicIncludeGraphNavigator.kt:40)
   at androidx.navigation.NavController.navigate(NavController.java:1049)
   at androidx.navigation.NavController.navigate(NavController.java:935)
   at androidx.navigation.NavController.navigate(NavController.java:868)
   at androidx.navigation.NavController.navigate(NavController.java:854)
   at androidx.navigation.NavController.navigate(NavController.java:1107)
   at com.compass.corelibrary.extensions.NavControllerExtensionsKt.navigateSafeSource(NavControllerExtensions.kt:18)

My app's build.gradle file is like this

apply plugin: 'com.android.application'
apply plugin: 'com.google.firebase.firebase-perf'
apply plugin: 'com.heapanalytics.android'
apply plugin: 'kotlin-android'
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'com.compass.jacoco.jacoco-android'
apply plugin: "androidx.navigation.safeargs.kotlin"
apply plugin: 'kotlin-parcelize'
apply plugin: 'kotlin-kapt'
apply plugin: 'jacoco'
apply plugin: 'kotlinx-serialization'

ext.versionMajor = 2
ext.versionMinor = 35
ext.versionPatch = 0
ext.minimumSdkVersion = 21

android {
    compileSdkVersion 31

    defaultConfig {
        applicationId "com.sample.sample"
        minSdkVersion project.ext.minimumSdkVersion
        targetSdkVersion 31
        versionCode Integer.parseInt(project.VERSION_CODE)
        versionName project.VERSION_NAME
        testInstrumentationRunner "com.sample.SampleAndroidJUnitRunner"
        ext {
            heapEnabled = true
            heapAutoInit = true
            heapEnvId = HEAP_KEY_GAMMA
        }
    }
    signingConfigs {
        beta {
            keyAlias "circleci-beta-key-alias"
            keyPassword "password"
            storeFile file("circleci-beta-key.keystore")
            storePassword "password"
        }
    }
    packagingOptions {
        exclude 'META-INF/DEPENDENCIES'
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/LICENSE.txt'
        exclude 'META-INF/license.txt'
        exclude 'META-INF/NOTICE'
        exclude 'META-INF/NOTICE.txt'
        exclude 'META-INF/notice.txt'
        exclude 'META-INF/ASL2.0'
        exclude 'META-INF/kotlinx-coroutines-core.kotlin_module'
        exclude 'META-INF/kotlinx-serialization-runtime.kotlin_module'
    }
    flavorDimensions "version"
    productFlavors {
        debugFlavor {
            getIsDefault().set(true)
            dimension "version"
            applicationIdSuffix ".debug"
            matchingFallbacks = ["release", "debug"]
            manifestPlaceholders = [
                    auth0Domain: "@string/com_auth0_domain_staging",
                    auth0Scheme: "sample",
                    facebookLoginProtocolScheme: "@string/fb_login_protocol_scheme_staging",
                    facebookAppId: "@string/facebook_app_id_staging",
                    facebookProvider: "@string/facebook_provider_staging"
            ]

        }
        alphaFlavor {
            dimension "version"
            applicationIdSuffix ".alpha"
            matchingFallbacks = ["release", "debug"]
            manifestPlaceholders = [
                    auth0Domain: "@string/com_auth0_domain_staging",
                    auth0Scheme: "sample",
                    facebookLoginProtocolScheme: "@string/fb_login_protocol_scheme_staging",
                    facebookAppId: "@string/facebook_app_id_staging",
                    facebookProvider: "@string/facebook_provider_staging"
            ]
        }
        betaFlavor {
            dimension "version"
            applicationIdSuffix ".beta"
            matchingFallbacks = ["release", "debug"]
            manifestPlaceholders = [
                    auth0Domain: "@string/com_auth0_domain_staging",
                    auth0Scheme: "sample",
                    facebookLoginProtocolScheme: "@string/fb_login_protocol_scheme_staging",
                    facebookAppId: "@string/facebook_app_id_staging",
                    facebookProvider: "@string/facebook_provider_staging"
            ]
        }
        rcFlavor {
            dimension "version"
            applicationIdSuffix ".rc"
            matchingFallbacks = ["release", "debug"]
            manifestPlaceholders = [
                    auth0Domain: "@string/com_auth0_domain_staging",
                    auth0Scheme: "sample",
                    facebookLoginProtocolScheme: "@string/fb_login_protocol_scheme_staging",
                    facebookAppId: "@string/facebook_app_id_staging",
                    facebookProvider: "@string/facebook_provider_staging"
            ]
        }
        playStoreFlavor {
            dimension "version"
            matchingFallbacks = ["release", "debug"]
            manifestPlaceholders = [
                    auth0Domain: "@string/com_auth0_domain_prod",
                    auth0Scheme: "compass",
                    facebookLoginProtocolScheme: "@string/fb_login_protocol_scheme_prod",
                    facebookAppId: "@string/facebook_app_id_prod",
                    facebookProvider: "@string/facebook_provider_prod"
            ]
            ext.heapEnvId = HEAP_KEY_PRODUCTION
        }
    }
    buildTypes {
        debug {
            getIsDefault().set(true)
            debuggable true
            multiDexEnabled true
            signingConfig signingConfigs.beta
            matchingFallbacks = ["release", "debug"]
            buildConfigField "boolean", "ENABLE_LEAK_CANARY", enableLeakCanary
            testCoverageEnabled false
            FirebasePerformance {
                instrumentationEnabled false
            }
        }
        release {
            minifyEnabled true
            shrinkResources true
            productFlavors.alphaFlavor.signingConfig signingConfigs.beta
            productFlavors.betaFlavor.signingConfig signingConfigs.beta
            productFlavors.rcFlavor.signingConfig signingConfigs.beta
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            matchingFallbacks = ["release", "debug"]
        }
    }
    dexOptions {
        javaMaxHeapSize "4g"
    }

    testOptions {
        unitTests {
            includeAndroidResources = true
            // Added to ensure timezone is America/New_York for testing purposes
            all{
                jvmArgs '-Duser.timezone=America/New_York'
                systemProperty 'robolectric.dependency.repo.url', 'https://repo1.maven.org/maven2'

            }
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    sourceSets {
        androidTest { java.srcDirs = ['src/androidTest/kotlin'] }
    }

    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_1_8.toString()
    }

    buildFeatures {
        viewBinding true
        dataBinding true
        compose true
    }
    composeOptions {
        kotlinCompilerExtensionVersion '1.0.5'
    }
    dynamicFeatures = [':dynamicfeature']

    preBuild.dependsOn ktlintFormat
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    //All the dependencies are here.
}

My Dynamic Feature build.gradle looks like

apply plugin: 'com.android.dynamic-feature'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'androidx.navigation.safeargs.kotlin'

android {
    compileSdk 31

    defaultConfig {
        minSdk 21
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        debug {
            getIsDefault().set(true)
            debuggable true
            matchingFallbacks = ["release", "debug"]
        }
        release {
            matchingFallbacks = ["release", "debug"]
        }
    }
    flavorDimensions "version"
    productFlavors {
        debugFlavor {
            getIsDefault().set(true)
            dimension "version"
            matchingFallbacks = ["release", "debug"]
        }
        alphaFlavor {
            dimension "version"
            matchingFallbacks = ["release", "debug"]
        }
        betaFlavor {
            dimension "version"
            matchingFallbacks = ["release", "debug"]
        }
        rcFlavor {
            dimension "version"
            matchingFallbacks = ["release", "debug"]
        }
        playStoreFlavor {
            dimension "version"
            matchingFallbacks = ["release", "debug"]
        }
    }
    buildFeatures {
        viewBinding true
        dataBinding true
    }
}

dependencies {
    implementation project(':app')
}

I am navigating to Fragment2 which is in "dynamicfeature" from Fragment1 which is in "app". Fragment1 is hosted by MainActivity which is in "app".

My app's navigation graph has entry as

<include-dynamic
        android:id="@+id/me_dynamic_feature"
        app:moduleName="dynamicfeature"
        app:graphResName="dynamic_feature_nav"
        app:graphPackage="${applicationId}.dynamicfeature" />

Solution

  • I was able to resolve this issue. Apparently, if you use <include-dynamic> tag for navigating into the dynamic feature module. You have to specify ProgressFragment which extends AbstractProgressFragment and specify it as app:progressDestination.

    Also since Fragment1 is in base app, which is hosted by activity in base app. We need to override attachBaseContext method in this activity too like below. This will fix the case where same exception was occurring at the subsequent launch.

    override fun attachBaseContext(newBase: Context) {
        super.attachBaseContext(newBase)
        SplitCompat.installActivity(this)
    }