模块化导航代码

本页介绍了如何将导航代码模块化。本指南旨在补充应用模块化常规指南

概览

对导航代码进行模块化处理是指将相关的导航键及其所代表的内容拆分为单独的模块。这样可以明确划分职责,并让您在应用的不同功能之间进行导航。

如需将导航代码模块化,请执行以下操作:

  • 为应用中的每个功能创建两个子模块:apiimpl
  • 将每个功能的导航键放入其 api 模块中
  • 将每个功能的 entryProviders 和可导航内容放入关联的 impl 模块中
  • 直接或使用依赖项注入向主应用模块提供 entryProviders

将功能拆分为 API 和实现子模块

为应用中的每个功能创建两个名为 apiimpl(“实现”的缩写)的子模块。您可以使用下表来决定将导航代码放置在何处。

模块名称

包含

api

导航键

impl

相应功能的内容,包括 NavEntryentryProvider 的定义。另请参阅将键解析为内容

此方法允许一个功能通过以下方式导航到另一个功能:使其包含在 impl 模块中的内容依赖于另一个模块(包含在该模块的 api 模块中)的导航键。

功能模块依赖关系图,显示了 `impl` 模块如何依赖于 `api` 模块。
图 1. 功能模块依赖关系图,显示了实现模块如何依赖于 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() }
                },
                // ...
            )
        }
    }
}

如果导航条目需要导航(例如,包含可导航到新屏幕的界面元素),请将能够修改应用导航状态的对象注入到每个构建器函数中。

资源

如需查看展示如何模块化 Navigation 3 代码的代码示例,请参阅: