На этой странице вы найдете руководство по модульной организации кода навигации. Оно дополняет общие рекомендации по модульной структуре приложений.
Обзор
Модуляризация кода навигации – это процесс разделения связанных ключей навигации и контента, который они представляют, на отдельные модули. Это обеспечивает четкое разделение обязанностей и позволяет перемещаться между различными функциями вашего приложения.
Чтобы модулизировать код навигации, выполните следующие действия:
- Создайте два подмодуля:
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 } } }
Вызовите эту функцию с помощью entryProvider DSL при определении 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 in Android
- Androidify