Эта страница представляет собой руководство по модульной организации кода навигации. Она призвана дополнить общие рекомендации по модульной организации приложений .
Обзор
Модульная структура кода навигации — это процесс разделения связанных навигационных клавиш и представляемого ими контента на отдельные модули. Это обеспечивает четкое разделение обязанностей и позволяет осуществлять навигацию между различными функциями вашего приложения.
Для модульной организации кода навигации выполните следующие действия:
- Создайте два подмодуля:
apiиimplдля каждой функции вашего приложения. - Разместите клавиши навигации для каждой функции в соответствующем модуле
api. - Разместите
entryProvidersи навигационное содержимое для каждой функции в соответствующем модулеimpl. - Предоставляйте
entryProvidersосновным модулям вашего приложения, либо напрямую, либо с помощью внедрения зависимостей.
Разделите функциональные возможности на подмодули API и реализации.
Для каждой функции вашего приложения создайте два подмодуля с именами 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 } } }
Вызывайте эту функцию, используя DSL entryProvider , при определении entryProvider в основном модуле вашего приложения.
// import androidx.navigation3.runtime.entryProvider // import androidx.navigation3.ui.NavDisplay NavDisplay( entryProvider = entryProvider { featureAEntryBuilder() }, // ... )
Используйте внедрение зависимостей для добавления записей в основное приложение.
В приведенном выше примере кода каждый конструктор записей вызывается непосредственно основным приложением с использованием DSL entryProvider . Если ваше приложение содержит много экранов или функциональных модулей, этот шаблон может плохо масштабироваться.
Для решения этой проблемы каждый функциональный модуль должен добавлять свои конструкторы входных данных в активность приложения с помощью внедрения зависимостей.
Например, следующий код использует множественные привязки 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, можно найти здесь:
- Рецепты кода архитектуры Navigation 3
- Процесс обучения модульному подходу в приложении Now для Android
- Androidify