应用无法找到 DynamicFeature 导航图。获取导航图的 Resources$NotFoundException
App is not able to find DynamicFeature navigation graph. Getting Resources$NotFoundException for the navigation graph
我正在为我的一项功能使用 Android 的动态交付。我已经分离了该功能的代码。我也在我的项目中使用导航组件。
我可以看到正在从进度条下载动态功能,下载后我正在使用导航组件导航到 Fragment2。
但是,当我尝试从我的“应用程序”中的 Fragment1 导航到我的“动态特征”中的 Fragment2 时,我遇到了异常。
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)
我的应用程序的 build.gradle 文件是这样的
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.
}
我的动态特征build.gradle看起来像
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')
}
我正在从“app”中的 Fragment1 导航到“dynamicfeature”中的 Fragment2。 Fragment1 由位于“app”中的 MainActivity 托管。
我的应用程序的导航图条目为
<include-dynamic
android:id="@+id/me_dynamic_feature"
app:moduleName="dynamicfeature"
app:graphResName="dynamic_feature_nav"
app:graphPackage="${applicationId}.dynamicfeature" />
我能够解决这个问题。显然,如果您使用 <include-dynamic>
标签导航到动态功能模块。您必须指定扩展 AbstractProgressFragment
的 ProgressFragment
并将其指定为 app:progressDestination
.
此外,由于 Fragment1 在基础应用中,它由基础应用中的 activity 托管。我们也需要覆盖 activity 中的 attachBaseContext
方法,如下所示。这将解决在后续启动时发生相同异常的情况。
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(newBase)
SplitCompat.installActivity(this)
}
我正在为我的一项功能使用 Android 的动态交付。我已经分离了该功能的代码。我也在我的项目中使用导航组件。
我可以看到正在从进度条下载动态功能,下载后我正在使用导航组件导航到 Fragment2。
但是,当我尝试从我的“应用程序”中的 Fragment1 导航到我的“动态特征”中的 Fragment2 时,我遇到了异常。
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)
我的应用程序的 build.gradle 文件是这样的
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.
}
我的动态特征build.gradle看起来像
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')
}
我正在从“app”中的 Fragment1 导航到“dynamicfeature”中的 Fragment2。 Fragment1 由位于“app”中的 MainActivity 托管。
我的应用程序的导航图条目为
<include-dynamic
android:id="@+id/me_dynamic_feature"
app:moduleName="dynamicfeature"
app:graphResName="dynamic_feature_nav"
app:graphPackage="${applicationId}.dynamicfeature" />
我能够解决这个问题。显然,如果您使用 <include-dynamic>
标签导航到动态功能模块。您必须指定扩展 AbstractProgressFragment
的 ProgressFragment
并将其指定为 app:progressDestination
.
此外,由于 Fragment1 在基础应用中,它由基础应用中的 activity 托管。我们也需要覆盖 activity 中的 attachBaseContext
方法,如下所示。这将解决在后续启动时发生相同异常的情况。
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(newBase)
SplitCompat.installActivity(this)
}