本页面介绍了如何对导航代码进行模块化处理。它旨在 补充应用模块化的一般指南。
概览
对导航代码进行模块化处理就是将相关导航键及其所代表的内容拆分成单独模块的过程。这样可以明确划分职责,并让您在应用的不同功能之间进行导航。
如需对导航代码进行模块化处理,请执行以下操作:
- 为应用中的每个功能创建两个子模块:
api和impl - 将每个功能的导航键放入其
api模块中 - 将每个功能的
entryProviders和可导航内容放入关联的impl模块中 - 直接或使用依赖项注入向主应用模块提供
entryProviders
将功能拆分为 api 和实现子模块
对于应用中的每个功能,创建两个名为 api 和 impl(“实现”的缩写)的子模块。使用下表确定导航代码的放置位置。
模块名称 |
包含 |
|
|
|
该功能的内容,包括
|
这种方法允许一个功能导航到另一个功能,方法是允许其 impl 模块中包含的内容依赖于另一个模块的导航键,而这些导航键包含在该模块的 api 模块中。
使用扩展函数分离导航条目
在 Navigation 3 中,可导航内容是使用导航条目定义的。如需将这些条目拆分为单独的模块,请在
EntryProviderScope上创建扩展函数,并将它们移到该功能的impl模块中。
这些函数称为条目构建器 。
以下代码示例展示了一个构建两个导航条目的条目构建器。
// import androidx.navigation3.runtime.EntryProviderScope // import androidx.navigation3.runtime.NavKey fun EntryProviderScope<NavKey>.featureAEntryBuilder() { entry<KeyA> { ContentRed("Screen A") { // Content for screen A } } entry<KeyA2> { ContentGreen("Screen A2") { // Content for screen A2 } } }
在主应用模块中定义
entryProvider 时,使用 entryProvider DSL 调用该函数。
// import androidx.navigation3.runtime.entryProvider // import androidx.navigation3.ui.NavDisplay NavDisplay( entryProvider = entryProvider { featureAEntryBuilder() }, // ... )
使用依赖项注入将条目添加到主应用
在前面的代码示例中,每个条目构建器都由主应用使用 entryProvider DSL 直接调用。如果您的应用有很多屏幕或功能模块,此模式可能无法很好地扩缩。
为解决此问题,请让每个功能模块使用依赖项注入将其条目构建器贡献到应用 activity 中。
例如,以下代码使用 Dagger 多重绑定(
具体而言是 @IntoSet)将条目构建器注入到 Set 拥有的
MainActivity 中。然后,这些条目在 entryProvider 中以迭代方式调用,从而无需显式调用多个条目构建器函数。
功能模块
// import dagger.Module // import dagger.Provides // import dagger.hilt.InstallIn // import dagger.hilt.android.components.ActivityRetainedComponent // import dagger.multibindings.IntoSet @Module @InstallIn(ActivityRetainedComponent::class) object FeatureAModule { @IntoSet @Provides fun provideFeatureAEntryBuilder() : EntryProviderScope<NavKey>.() -> Unit = { featureAEntryBuilder() } }
应用模块
// import android.os.Bundle // import androidx.activity.ComponentActivity // import androidx.activity.compose.setContent // import androidx.navigation3.runtime.EntryProviderScope // import androidx.navigation3.runtime.NavKey // import androidx.navigation3.runtime.entryProvider // import androidx.navigation3.ui.NavDisplay // import javax.inject.Inject class MainActivity : ComponentActivity() { @Inject lateinit var entryBuilders: Set<@JvmSuppressWildcards EntryProviderScope<NavKey>.() -> Unit> override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { NavDisplay( entryProvider = entryProvider { entryBuilders.forEach { builder -> this.builder() } }, // ... ) } } }
如果您的导航条目需要导航(例如,它们包含导航到新屏幕的界面元素),请将能够修改应用导航状态的对象注入到每个构建器函数中。
对深层链接匹配器进行模块化处理
如果您的应用支持 深层链接 并且已模块化,则每个功能模块
应为其拥有的目标定义 DeepLinkMatcher 实例。
如需整理应用中的所有匹配器,您可以使用依赖项注入多重绑定。例如,每个功能模块都可以将其匹配器贡献给 Dagger @IntoSet 多重绑定:
功能模块
@Module @InstallIn(ActivityRetainedComponent::class) object FeatureADeepLinkModule { @IntoSet @Provides fun provideUserMatcher(): DeepLinkMatcher<*, *> { return UriDeepLinkMatcher( DeepLinkUri("www.example.com/users/{id}"), serializer<UserProfileKey>() ) } }
然后,在主应用模块(例如 MainActivity)中,您可以注入匹配器集并使用它们来匹配传入的请求:
应用模块
@AndroidEntryPoint class MainActivityWithDI : ComponentActivity() { @Inject lateinit var deepLinkMatchers: Set<@JvmSuppressWildcards DeepLinkMatcher<*, *>> override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val request = DeepLinkRequest(intent = intent) val matchResult = deepLinkMatchers .mapNotNull { it.match(request) } .maxOrNull() val backStack = when (matchResult) { null -> listOf(HomeKey) is BackStackMatchResult<*, *> -> { @Suppress("UNCHECKED_CAST") matchResult.backStack as List<NavKey> } else -> listOf(matchResult.key) } } }
如需详细了解如何定义和处理深层链接,请参阅支持深层 链接。
资源
如需查看展示如何对 Navigation 3 代码进行模块化处理的代码示例,请参阅: