本页是一份指南,旨在帮助您将导航代码模块化。旨在对应用模块化的一般指南进行补充。
概览
模块化导航代码是指将相关的导航键及其代表的内容分离到各个模块中的过程。这样可以明确划分职责,并让您在应用的不同功能之间导航。
如需对导航代码进行模块化处理,请执行以下操作:
- 为应用中的每个功能创建两个子模块:
api和impl - 将每个功能的导航键放入其
api模块中 - 将每项功能的
entryProviders和可导航内容放入关联的impl模块中 - 直接或使用依赖项注入向主应用模块提供
entryProviders
将功能拆分为 api 和 implementation 子模块
对于应用中的每个功能,创建两个名为 api 和 impl(“implementation”的缩写)的子模块。请参考下表来决定导航代码的放置位置。
模块名称 |
包含 |
|
|
|
该功能的内容,包括 |
这种方法允许一个功能导航到另一个功能,方法是允许其内容(包含在其 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)将条目构建器注入 MainActivity 拥有的 Set。然后,这些函数会在 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 代码模块化的代码示例,请参阅: