На этой странице представлены несколько лучших практик и рекомендаций по архитектуре . Примените их, чтобы улучшить качество, надежность и масштабируемость вашего приложения. Они также упростят его поддержку и тестирование.
Приведенные ниже лучшие практики сгруппированы по темам. Каждой рекомендации присвоен приоритет, отражающий ее важность. Список приоритетов выглядит следующим образом:
- Настоятельно рекомендуется: внедрите эту практику, если она не противоречит вашему подходу в корне.
- Рекомендуется: Эта практика, вероятно, улучшит ваше приложение.
- Необязательно: Эта практика может улучшить ваше приложение в определенных обстоятельствах.
Многоуровневая архитектура
Рекомендуемая нами многоуровневая архитектура способствует разделению задач. Она отделяет пользовательский интерфейс от моделей данных, соответствует принципу единого источника истины и следует принципам однонаправленного потока данных . Вот несколько рекомендаций по многоуровневой архитектуре:
| Рекомендация | Описание |
|---|---|
| Используйте четко определенный слой данных . Настоятельно рекомендуется | Слой данных предоставляет доступ к данным приложения остальной части приложения и содержит подавляющее большинство бизнес-логики вашего приложения.
|
| Используйте четко определенный слой пользовательского интерфейса . Настоятельно рекомендуется | Слой пользовательского интерфейса отображает данные приложения на экране и служит основной точкой взаимодействия с пользователем. Jetpack Compose — это рекомендуемый современный инструментарий для создания пользовательского интерфейса вашего приложения.
|
| Предоставьте доступ к данным приложения из уровня данных с помощью репозитория. Настоятельно рекомендуется | Убедитесь, что компоненты пользовательского интерфейса, такие как компонуемые объекты или модели представления, не взаимодействуют напрямую с источником данных. Примеры источников данных включают:
|
| Используйте сопрограммы и потоки выполнения . Настоятельно рекомендуется | Используйте сопрограммы и потоки для взаимодействия между уровнями. Для получения дополнительной информации о лучших практиках использования сопрограмм см. раздел «Лучшие практики использования сопрограмм в Android» . |
| Используйте доменный слой . Рекомендуется для крупных приложений | Используйте слой предметной области с вариантами использования, если вам необходимо повторно использовать бизнес-логику, взаимодействующую со слоем данных, в нескольких ViewModel, или если вы хотите упростить сложность бизнес-логики конкретной ViewModel. |
слой пользовательского интерфейса
Роль пользовательского интерфейса заключается в отображении данных приложения на экране и выполнении функции основной точки взаимодействия с пользователем. Вот несколько рекомендаций по работе с пользовательским интерфейсом:
| Рекомендация | Описание |
|---|---|
| Следуйте однонаправленному потоку данных (UDF) . Настоятельно рекомендуется | Следуйте принципам однонаправленного потока данных (UDF) , где ViewModels предоставляют доступ к состоянию пользовательского интерфейса, используя шаблон наблюдателя, и получают действия от пользовательского интерфейса посредством вызовов методов. |
| Используйте AAC ViewModels, если их преимущества применимы к вашему приложению. Настоятельно рекомендуется | Используйте AAC ViewModels для обработки бизнес-логики и получения данных приложения, чтобы предоставить пользователю доступ к состоянию пользовательского интерфейса. Для получения дополнительной информации о передовых методах работы с ViewModel см. раздел «Рекомендации по архитектуре». Для получения дополнительной информации о преимуществах ViewModel см. статью «ViewModel как хранилище состояния бизнес-логики». |
| Используйте сбор состояний пользовательского интерфейса с учетом жизненного цикла. Настоятельно рекомендуется | Получайте состояние пользовательского интерфейса из самого интерфейса, используя соответствующий построитель сопрограмм, учитывающий жизненный цикл, collectAsStateWithLifecycle . Подробнее о функции |
| Не отправляйте события из ViewModel в пользовательский интерфейс. Настоятельно рекомендуется | Обработайте событие немедленно в ViewModel и вызовите обновление состояния с результатом обработки события. Для получения дополнительной информации о событиях пользовательского интерфейса см. раздел « Обработка событий ViewModel» . |
| Используйте приложение, предназначенное для выполнения одной задачи. Настоятельно рекомендуется | Используйте Navigation 3 для навигации между экранами и создания прямых ссылок на ваше приложение, если оно состоит из нескольких экранов. |
| Используйте Jetpack Compose . Настоятельно рекомендуется | Используйте Jetpack Compose для создания новых приложений для телефонов, планшетов, складных устройств и Wear OS. |
В следующем фрагменте кода показано, как собирать состояние пользовательского интерфейса с учетом жизненного цикла:
@Composable
fun MyScreen(
viewModel: MyViewModel = viewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
}
ViewModel
ViewModels отвечают за предоставление состояния пользовательского интерфейса и доступ к слою данных. Вот несколько рекомендаций по использованию ViewModels:
| Рекомендация | Описание |
|---|---|
| Сохраняйте независимость ViewModel от жизненного цикла Android. Настоятельно рекомендуется | В ViewModel не следует хранить ссылки на какие-либо типы, связанные с жизненным циклом. Не передавайте Activity , Context или Resources в качестве зависимостей. Если чему-то в ViewModel требуется Context , тщательно проверьте, находится ли это на нужном уровне. |
| Используйте сопрограммы и потоки выполнения . Настоятельно рекомендуется | Модель представления взаимодействует с уровнями данных или предметной области следующим образом:
|
| Используйте ViewModel на уровне экрана. Настоятельно рекомендуется | Не используйте ViewModels в многократно используемых элементах пользовательского интерфейса. ViewModels следует использовать в следующих случаях:
|
| Используйте простые классы-хранилища состояния в многократно используемых компонентах пользовательского интерфейса. Настоятельно рекомендуется | Для обработки сложных данных в многократно используемых компонентах пользовательского интерфейса используйте простые классы-хранилища состояния . В этом случае состояние можно будет перемещать и контролировать извне. |
Не используйте AndroidViewModel . Рекомендуется | Используйте класс ViewModel , а не AndroidViewModel . Не используйте класс Application в ViewModel. Вместо этого перенесите зависимость в пользовательский интерфейс или слой данных. |
| Предоставьте доступ к состоянию пользовательского интерфейса. Рекомендуется | Сделайте так, чтобы ваши ViewModel предоставляли данные пользовательскому интерфейсу через одно свойство с именем uiState . Если пользовательский интерфейс отображает несколько несвязанных между собой элементов данных, ViewModel может предоставлять несколько свойств состояния пользовательского интерфейса .
|
В следующем фрагменте кода показано, как предоставить доступ к состоянию пользовательского интерфейса из ViewModel:
@HiltViewModel
class BookmarksViewModel @Inject constructor(
newsRepository: NewsRepository
) : ViewModel() {
val feedState: StateFlow<NewsFeedUiState> =
newsRepository
.getNewsResourcesStream()
.mapToFeedState(savedNewsResourcesState)
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = NewsFeedUiState.Loading
)
// ...
}
Жизненный цикл
Следуйте передовым методам работы с жизненным циклом действий :
| Рекомендация | Описание |
|---|---|
Используйте эффекты, учитывающие жизненный цикл, в компонуемых объектах вместо переопределения коллбэков жизненного цикла Activity . Настоятельно рекомендуется | Не следует переопределять методы жизненного цикла
|
В следующем фрагменте кода описано, как выполнять операции, учитывая определенное состояние жизненного цикла:
@Composable
fun LocationChangedEffect(
locationManager: LocationManager,
onLocationChanged: (Location) -> Unit
) {
val currentOnLocationChanged by rememberUpdatedState(onLocationChanged)
LifecycleStartEffect(locationManager) {
val listener = LocationListener { newLocation ->
currentOnLocationChanged(newLocation)
}
try {
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
1000L,
1f,
listener,
)
} catch (e: SecurityException) {
// TODO: Handle missing permissions
}
onStopOrDispose {
locationManager.removeUpdates(listener)
}
}
}
Обработка зависимостей
При управлении зависимостями между компонентами следуйте передовым практикам:
| Рекомендация | Описание |
|---|---|
| Используйте внедрение зависимостей . Настоятельно рекомендуется | Используйте лучшие практики внедрения зависимостей , в основном, по возможности, внедрение через конструктор . |
| При необходимости ограничьте область действия отдельным компонентом. Настоятельно рекомендуется | Область видимости ограничивается контейнером зависимостей , если тип содержит изменяемые данные, которые необходимо совместно использовать, или если инициализация типа является ресурсоемкой и широко используется в приложении. |
| Используйте рукоять . Рекомендуется | В простых приложениях используйте Hilt или ручное внедрение зависимостей . Hilt следует использовать, если ваш проект достаточно сложен — например, если он включает в себя что-либо из перечисленного ниже:
|
Тестирование
Ниже приведены некоторые рекомендации по тестированию :
| Рекомендация | Описание |
|---|---|
| Знайте, что нужно проверять . Настоятельно рекомендуется | Если проект не сводится к простому приложению "Hello World", протестируйте его. Как минимум, включите следующее:
|
| Предпочитайте подделки насмешкам. Настоятельно рекомендуется | Для получения дополнительной информации об использовании поддельных файлов см. раздел «Использование тестовых дубликатов в Android» . |
| Тестирование потоков состояний. Настоятельно рекомендуется | При тестировании StateFlow выполните следующие действия:
|
Для получения дополнительной информации см. разделы «Что тестировать в Android» и «Тестируйте макет Compose» .
Модели
При разработке моделей для ваших приложений соблюдайте следующие рекомендации:
| Рекомендация | Описание |
|---|---|
| В сложных приложениях создавайте модель для каждого слоя. Рекомендуется | В сложных приложениях создавайте новые модели на разных уровнях или в разных компонентах, когда это целесообразно. Рассмотрим следующие примеры:
|
Правила именования
При именовании кода следует учитывать следующие рекомендации:
| Рекомендация | Описание |
|---|---|
| Методы именования. Необязательный | Используйте глагольные фразы для именования методов — например, makePayment() . |
| Наименование свойств. Необязательный | Используйте именные группы для обозначения свойств — например, inProgressTopicSelection . |
| Именование потоков данных. Необязательный | Когда класс предоставляет доступ к потоку Flow или любому другому потоку, используется соглашение об именовании get{model}Stream . Например, getAuthorStream(): Flow<Author> . Если функция возвращает список моделей, используйте имя модели во множественном числе: getAuthorsStream(): Flow<List<Author>> . |
| Реализации интерфейсов именования. Необязательный | Используйте осмысленные имена для реализаций интерфейсов. Используйте префикс Default , если не удается найти более подходящее имя. Например, для интерфейса NewsRepository может использоваться OfflineFirstNewsRepository или InMemoryNewsRepository . Если подходящее имя найти не удается, используйте DefaultNewsRepository . Для фиктивных реализаций используйте префикс Fake , например, FakeAuthorsRepository . |
Дополнительные ресурсы
Для получения более подробной информации об архитектуре Android см. следующие дополнительные ресурсы:
Документация
- Архитектура пользовательского интерфейса Compose
- Архитектурное многослойное проектирование с помощью Jetpack Compose