Изучаем Android-разработку
Действительно общая модель просмотра в Jetpack Compose
Легкая навигация и внедрение зависимостей с помощью Hilt
Введение
Добро пожаловать в очаровательный мир Jetpack Compose, где декларативный пользовательский интерфейс воплощает в жизнь ваше Android-приложение с элегантностью и простотой. В этом завораживающем путешествии мы узнаем, как использовать силу Hilt, волшебника внедрения зависимостей, и использовать магию Composition Locals для упрощения навигации в вашем Android-приложении на основе Jetpack Compose. Приготовьтесь быть зачарованными, когда мы раскроем секреты архитектурного подхода, который экономит время разработки и создает кодовую базу, которую легче поддерживать.
Давайте сделаем это
1. Сила модели Hilt and View
Hilt — это надежная библиотека внедрения зависимостей, которая легко интегрируется с компонентами Jetpack, что делает ее идеальным выбором для управления зависимостями в современных приложениях для Android. Одним из основных преимуществ Hilt является его способность автоматически внедрять зависимости в классы модели представления с помощью @HiltViewModel
.
@HiltViewModel internal class AppViewModel @Inject constructor() : ViewModel() { lateinit var navController: NavHostController }
В нашем приложении мы используем AppViewModel
, который расширяет ViewModel
, для хранения и управления данными, связанными с пользовательским интерфейсом. Аннотируя класс AppViewModel
с помощью @HiltViewModel
, мы позволяем Hilt автоматически предоставлять экземпляры ViewModel
везде, где они необходимы в приложении. Это устраняет шаблонный код, такой как ручная ViewModel
заводская настройка, и гарантирует, что каждый ViewModel
правильно привязан к связанному с ним Composable.
2. Обработка навигации с помощью NavHostController
Jetpack Compose предоставляет мощный компонент навигации, который упрощает навигацию между различными экранами приложения. Для эффективного управления навигацией мы используем NavHostController
, основной компонент библиотеки навигации. Однако передача NavHostController
на каждый экран назначения может быть утомительной и подверженной ошибкам.
Чтобы решить эту проблему, мы представляем CompositionLocals, мощную функцию Jetpack Compose. С помощью LocalAppViewModel
мы создаем пользовательский CompositionLocal
, который позволяет нам совместно использовать экземпляр AppViewModel
и связанный с ним NavHostController
во всем приложении без явной передачи параметров. Используя CompositionLocalProvider
, мы гарантируем, что AppViewModelInfo
доступен для всех составных функций ниже иерархии, где используется LocalAppViewModel
.
internal val LocalAppViewModel = compositionLocalOf { AppViewModelInfo() } @Composable fun ProvideAppViewModel( navHostController: NavHostController, content: @Composable () -> Unit ) { val appViewModel: AppViewModel = hiltViewModel() appViewModel.navController = navHostController val appViewModelInfo = AppViewModelInfo(appViewModel) val state = remember { appViewModelInfo } CompositionLocalProvider( LocalAppViewModel provides state ) { content() } }
3. Собираем все вместе
В составной функции ProvideAppViewModel
мы объединяем возможности Hilt и CompositionLocals. Во-первых, мы используем hiltViewModel()
для получения экземпляра AppViewModel
, автоматически внедряемого Hilt. Затем мы присваиваем navHostController
, полученное в качестве параметра, свойству navController
элемента AppViewModel
. Эта ассоциация позволяет нам эффективно управлять навигацией между различными экранами без передачи параметров по воронке.
С помощью CompositionLocalProvider
мы предоставляем экземпляр AppViewModelInfo
дочерним составным функциям, делая AppViewModel
и связанный с ним navController
доступными во всей составной иерархии приложения. Эта архитектура значительно сокращает шаблонный код и обеспечивает более организованную и удобную в сопровождении кодовую базу.
Пример использования
Предположим, у нас есть три компонуемые функции, Screen A
Screen B
и Screen C
, представляющие разные экраны / компонуемые в нашем приложении.
@Composable fun ScreenA() { val navController = rememberNavController() // Using the new approach with Composition Locals to // provide the AppViewModel ProvideAppViewModel(navController) { NavHost(navController = navController, startDestination = AppRoutes.SCREEN_A.name) { composable(SCREEN_A.name) { ScreenA() } composable(SCREEN_B.name) { ScreenB() } composable(SCREEN_C.name) { ScreenC() } } } }
В старом способе передачи navController
вам пришлось бы явно передавать navController
в качестве параметра каждому составному объекту, который в нем нуждается. Например:
@Composable fun OldScreenB(navController: NavHostController) { // Access the navController in this composable } @Composable fun OldScreenC(navController: NavHostController) { // Access the navController in this composable }
С новым подходом, использующим Composition Locals, нам больше не нужно явно передавать navController
в качестве параметра каждому составному объекту, который его требует. Вместо этого мы предоставляем AppViewModel
и связанный с ним navController
с помощью CompositionLocalProvider
, и любая компонуемая функция ниже иерархии, где используется LocalAppViewModel
, может получить к ним прямой доступ.
@Composable fun ScreenB() { // No need to pass navController as a parameter // It is automatically available through Composition Locals // Access the AppViewModel and its navController in this composable and its children val appViewModelInfo = LocalAppViewModel.current val appViewModel = appViewModelInfo.appViewModel // Navigate back to ScreenA Button(onClick = { appViewModel?.navController?.popBackStack() }) { Text("Go back to Screen A") } }
Распространение этого подхода на темы
Универсальность CompositionLocals выходит за рамки внедрения ViewModel и навигации. Предположим, наше приложение поддерживает разные темы, например светлый и темный режимы. Мы можем использовать CompositionLocals для совместного использования текущих данных темы по всей компонуемой иерархии без необходимости явно передавать параметры и хранить их только в нашей модели представления. Мы можем еще больше расширить его, настроив его удаленно и всегда имея один источник достоверной информации о теме.
val theme = LocalAppViewModel.current.appViewModel.mytheme
Заключение
Использование Hilt для ViewModel
инъекции и CompositionLocals
для навигации в приложениях Jetpack Compose упрощает процесс разработки, делая его более интуитивно понятным и эффективным. Используя возможности этих библиотек, мы избавляемся от громоздких задач настройки и сосредотачиваемся на написании чистого и лаконичного кода. Полученная архитектура не только упрощает управление зависимостями, но и повышает читабельность кода.
В заключение, использование Hilt и CompositionLocals — это мощный шаг к созданию современных, надежных и удобных в сопровождении приложений для Android, которые сияют великолепием парадигмы декларативного пользовательского интерфейса Jetpack Compose. Итак, зачем ждать? Начните внедрять эти концепции в свои проекты и станьте свидетелем совершенно нового уровня разработки приложений для Android.
Примечание. Правильное использование локальных переменных композиции
Хотя Composition Locals предлагает мощный способ обмена данными и зависимостями по компонуемой иерархии в Jetpack Compose, важно использовать их разумно и избегать чрезмерного использования. Локальные объекты композиции наиболее эффективны при использовании на уровне приложения или в композициях родительского уровня, где данные или зависимости должны быть доступны для нескольких дочерних компонуемых функций.
Мысли?
Надеюсь, вам понравилось читать эту статью и вы узнали что-то новое или интересное!
Если у вас есть какие-либо вопросы или мысли, которыми вы можете поделиться, не стесняйтесь оставлять комментарии ниже. Я хотел бы услышать ваш опыт и идеи, поскольку мы продолжаем открывать бесконечные возможности Jetpack Compose вместе.
Гитхаб
В моей последней статье я создал приложение для виртуального паспорта национальных парков, в котором я придумал этот код и стратегию. Вот ссылка на репозиторий и Kotlin File, который ее использует.
Соединять!
Не стесняйтесь подписываться на меня в LinkedIn, чтобы получать регулярные обновления многих других статей, которые я собираюсь написать о Compose и Android в целом. Я всегда стремлюсь к общению с другими разработчиками, обмену знаниями и созданию динамичного обучающего сообщества.
Удачного кодирования!