На этой странице представлены несколько лучших практик и рекомендаций по архитектуре . Используйте их, чтобы улучшить качество, надежность и масштабируемость вашего приложения. Они также упрощают поддержку и тестирование вашего приложения.
Рекомендации ниже сгруппированы по темам. У каждого есть приоритет, который отражает, насколько сильно его рекомендует команда. Список приоритетов выглядит следующим образом:
- Настоятельно рекомендуется: вам следует применять эту практику, если она не противоречит фундаментальному вашему подходу.
- Рекомендуется: эта практика может улучшить ваше приложение.
- Необязательно: эта практика может улучшить ваше приложение в определенных обстоятельствах.
Многоуровневая архитектура
Рекомендуемая нами многоуровневая архитектура способствует разделению задач. Он управляет пользовательским интерфейсом на основе моделей данных, соответствует принципу единого источника истины и принципам однонаправленного потока данных . Вот некоторые рекомендации по многоуровневой архитектуре:
Рекомендация | Описание |
---|---|
Используйте четко определенный уровень данных . Настоятельно рекомендуется | Уровень данных предоставляет данные приложения остальной части приложения и содержит большую часть бизнес-логики вашего приложения.
|
Используйте четко определенный уровень пользовательского интерфейса . Настоятельно рекомендуется | Уровень пользовательского интерфейса отображает данные приложения на экране и служит основной точкой взаимодействия с пользователем.
|
Уровень данных должен предоставлять данные приложения с помощью репозитория. Настоятельно рекомендуется | Компоненты на уровне пользовательского интерфейса, такие как компонуемые объекты, действия или модели представления, не должны напрямую взаимодействовать с источником данных. Примеры источников данных:
|
Используйте сопрограммы и потоки . Настоятельно рекомендуется | Используйте сопрограммы и потоки для взаимодействия между уровнями. |
Используйте слой домена . Рекомендуется в больших приложениях | Используйте уровень предметной области , варианты использования, если вам нужно повторно использовать бизнес-логику, которая взаимодействует с уровнем данных в нескольких моделях представления, или вы хотите упростить сложность бизнес-логики конкретной модели представления. |
Уровень пользовательского интерфейса
Роль уровня пользовательского интерфейса — отображать данные приложения на экране и служить основной точкой взаимодействия с пользователем. Вот несколько рекомендаций для уровня пользовательского интерфейса:
Рекомендация | Описание |
---|---|
Следуйте однонаправленному потоку данных (UDF) . Настоятельно рекомендуется | Следуйте принципам однонаправленного потока данных (UDF) , согласно которым модели представления предоставляют состояние пользовательского интерфейса с помощью шаблона наблюдателя и получают действия от пользовательского интерфейса посредством вызовов методов. |
Используйте AAC ViewModels, если их преимущества применимы к вашему приложению. Настоятельно рекомендуется | Используйте AAC ViewModels для обработки бизнес-логики и получения данных приложения, чтобы представить состояние пользовательского интерфейса в пользовательском интерфейсе (Compose или представления Android). Дополнительные рекомендации по ViewModel см. здесь. Ознакомьтесь с преимуществами ViewModels здесь. |
Используйте коллекцию состояний пользовательского интерфейса с учетом жизненного цикла. Настоятельно рекомендуется | Соберите состояние пользовательского интерфейса из пользовательского интерфейса с помощью соответствующего построителя сопрограмм, учитывающего жизненный цикл: repeatOnLifecycle в системе View и collectAsStateWithLifecycle в Jetpack Compose. Узнайте больше о Узнайте больше о |
Не отправляйте события из ViewModel в пользовательский интерфейс. Настоятельно рекомендуется | Немедленно обработайте событие в ViewModel и вызовите обновление состояния с результатом обработки события. Подробнее о событиях пользовательского интерфейса здесь . |
Используйте приложение с одним действием. Рекомендуется | Используйте фрагменты навигации или компоновку навигации для навигации между экранами и создания глубоких ссылок на ваше приложение, если ваше приложение имеет более одного экрана. |
Используйте Jetpack Compose . Рекомендуется | Используйте Jetpack Compose для создания новых приложений для телефонов, планшетов, складных устройств и Wear OS. |
В следующем фрагменте показано, как собирать состояние пользовательского интерфейса с учетом жизненного цикла:
Просмотры
class MyFragment : Fragment() {
private val viewModel: MyViewModel by viewModel()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect {
// Process item
}
}
}
}
}
Сочинять
@Composable
fun MyScreen(
viewModel: MyViewModel = viewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
}
Модель просмотра
Модели представления отвечают за предоставление состояния пользовательского интерфейса и доступ к уровню данных. Вот несколько лучших практик для ViewModels:
Рекомендация | Описание |
---|---|
Модели ViewModels не должны зависеть от жизненного цикла Android. Настоятельно рекомендуется | Модели ViewModels не должны содержать ссылки на какой-либо тип, связанный с жизненным циклом. Не передавайте Activity, Fragment, Context или Resources в качестве зависимости. Если чему-то нужен Context в ViewModel, вам следует тщательно оценить, находится ли он на правильном уровне. |
Используйте сопрограммы и потоки . Настоятельно рекомендуется | ViewModel взаимодействует со слоями данных или домена, используя:
|
Используйте ViewModels на уровне экрана. Настоятельно рекомендуется | Не используйте ViewModels в повторно используемых частях пользовательского интерфейса. Вы должны использовать ViewModels в:
|
Используйте классы-держатели простого состояния в повторно используемых компонентах пользовательского интерфейса. Настоятельно рекомендуется | Используйте классы-держатели простого состояния для решения сложных задач в повторно используемых компонентах пользовательского интерфейса. Поступая таким образом, государство можно поднять и контролировать извне. |
Не используйте AndroidViewModel . Рекомендуется | Используйте класс ViewModel , а не AndroidViewModel . Класс Application не следует использовать в ViewModel. Вместо этого переместите зависимость в пользовательский интерфейс или на уровень данных. |
Раскройте состояние пользовательского интерфейса. Рекомендуется | Модели ViewModels должны предоставлять данные пользовательскому интерфейсу через одно свойство, называемое uiState . Если пользовательский интерфейс отображает несколько несвязанных фрагментов данных, виртуальная машина может предоставить несколько свойств состояния пользовательского интерфейса .
|
В следующем фрагменте показано, как предоставить состояние пользовательского интерфейса из 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
)
// ...
}
Жизненный цикл
Ниже приведены некоторые рекомендации по работе с жизненным циклом Android :
Рекомендация | Описание |
---|---|
Не переопределяйте методы жизненного цикла в действиях или фрагментах. Настоятельно рекомендуется | Не переопределяйте методы жизненного цикла, такие как onResume в действиях или фрагментах. Вместо этого используйте LifecycleObserver . Если приложению необходимо выполнить работу, когда жизненный цикл достигает определенного Lifecycle.State , используйте API repeatOnLifecycle . |
В следующем фрагменте показано, как выполнять операции в определенном состоянии жизненного цикла:
Просмотры
class MyFragment: Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {
override fun onResume(owner: LifecycleOwner) {
// ...
}
override fun onPause(owner: LifecycleOwner) {
// ...
}
}
}
}
Сочинять
@Composable
fun MyApp() {
val lifecycleOwner = LocalLifecycleOwner.current
DisposableEffect(lifecycleOwner, ...) {
val lifecycleObserver = object : DefaultLifecycleObserver {
override fun onStop(owner: LifecycleOwner) {
// ...
}
}
lifecycleOwner.lifecycle.addObserver(lifecycleObserver)
onDispose {
lifecycleOwner.lifecycle.removeObserver(lifecycleObserver)
}
}
}
Обработка зависимостей
Существует несколько рекомендаций, которые следует соблюдать при управлении зависимостями между компонентами:
Рекомендация | Описание |
---|---|
Используйте внедрение зависимостей . Настоятельно рекомендуется | Используйте лучшие практики внедрения зависимостей , в основном внедрение конструктора, когда это возможно. |
При необходимости перейдите к компоненту. Настоятельно рекомендуется | Область действия — контейнер зависимостей , если тип содержит изменяемые данные, которые необходимо совместно использовать, или тип требует больших затрат на инициализацию и широко используется в приложении. |
Используйте Хилт . Рекомендуется | Используйте Hilt или ручное внедрение зависимостей в простых приложениях. Используйте Hilt , если ваш проект достаточно сложен. Например, если у вас есть:
|
Тестирование
Ниже приведены некоторые рекомендации по тестированию :
Рекомендация | Описание |
---|---|
Знайте, что тестировать . Настоятельно рекомендуется | Если проект не такой же простой, как приложение hello world, вам следует протестировать его, как минимум, с помощью:
|
Предпочитайте подделки издевательствам. Настоятельно рекомендуется | Подробнее читайте в разделе «Использование тестовых двойников» в документации Android . |
Тестирование StateFlows. Настоятельно рекомендуется | При тестировании StateFlow :
|
Для получения дополнительной информации ознакомьтесь с руководством «Что тестировать в Android DAC» .
Модели
Вам следует соблюдать следующие рекомендации при разработке моделей в ваших приложениях:
Рекомендация | Описание |
---|---|
Создавайте модели для каждого слоя в сложных приложениях. Рекомендуется | В сложных приложениях создавайте новые модели в разных слоях или компонентах, когда это имеет смысл. Рассмотрим следующие примеры:
|
Соглашения об именах
При присвоении имени вашей кодовой базе вы должны учитывать следующие рекомендации:
Рекомендация | Описание |
---|---|
Методы именования. Необязательный | Методы должны быть глагольной группой. Например, makePayment() . |
Именование свойств. Необязательный | Свойства должны быть существительными. Например, inProgressTopicSelection . |
Именование потоков данных. Необязательный | Когда класс предоставляет поток Flow, LiveData или любой другой поток, соглашение об именах — get{model}Stream() . Например, getAuthorStream(): Flow<Author> Если функция возвращает список моделей, имя модели должно быть во множественном числе: getAuthorsStream(): Flow<List<Author>> |
Именование реализаций интерфейсов. Необязательный | Имена реализаций интерфейсов должны быть осмысленными. Используйте префикс Default , если лучшее имя не найдено. Например, для интерфейса NewsRepository у вас может быть OfflineFirstNewsRepository или InMemoryNewsRepository . Если вы не можете найти хорошее имя, используйте DefaultNewsRepository . Поддельные реализации должны иметь префикс Fake , как в FakeAuthorsRepository . |
На этой странице представлены несколько лучших практик и рекомендаций по архитектуре . Используйте их, чтобы улучшить качество, надежность и масштабируемость вашего приложения. Они также упрощают поддержку и тестирование вашего приложения.
Рекомендации ниже сгруппированы по темам. У каждого есть приоритет, который отражает, насколько сильно его рекомендует команда. Список приоритетов выглядит следующим образом:
- Настоятельно рекомендуется: вам следует применять эту практику, если она не противоречит фундаментальному вашему подходу.
- Рекомендуется: эта практика может улучшить ваше приложение.
- Необязательно: эта практика может улучшить ваше приложение в определенных обстоятельствах.
Многоуровневая архитектура
Рекомендуемая нами многоуровневая архитектура способствует разделению задач. Он управляет пользовательским интерфейсом на основе моделей данных, соответствует принципу единого источника истины и принципам однонаправленного потока данных . Вот некоторые рекомендации по многоуровневой архитектуре:
Рекомендация | Описание |
---|---|
Используйте четко определенный уровень данных . Настоятельно рекомендуется | Уровень данных предоставляет данные приложения остальной части приложения и содержит большую часть бизнес-логики вашего приложения.
|
Используйте четко определенный уровень пользовательского интерфейса . Настоятельно рекомендуется | Уровень пользовательского интерфейса отображает данные приложения на экране и служит основной точкой взаимодействия с пользователем.
|
Уровень данных должен предоставлять данные приложения с помощью репозитория. Настоятельно рекомендуется | Компоненты на уровне пользовательского интерфейса, такие как компонуемые объекты, действия или модели представления, не должны напрямую взаимодействовать с источником данных. Примеры источников данных:
|
Используйте сопрограммы и потоки . Настоятельно рекомендуется | Используйте сопрограммы и потоки для взаимодействия между уровнями. |
Используйте слой домена . Рекомендуется в больших приложениях | Используйте уровень предметной области , варианты использования, если вам нужно повторно использовать бизнес-логику, которая взаимодействует с уровнем данных в нескольких моделях представления, или вы хотите упростить сложность бизнес-логики конкретной модели представления. |
Уровень пользовательского интерфейса
Роль уровня пользовательского интерфейса — отображать данные приложения на экране и служить основной точкой взаимодействия с пользователем. Вот несколько рекомендаций для уровня пользовательского интерфейса:
Рекомендация | Описание |
---|---|
Следуйте однонаправленному потоку данных (UDF) . Настоятельно рекомендуется | Следуйте принципам однонаправленного потока данных (UDF) , согласно которым модели представления предоставляют состояние пользовательского интерфейса с помощью шаблона наблюдателя и получают действия от пользовательского интерфейса посредством вызовов методов. |
Используйте AAC ViewModels, если их преимущества применимы к вашему приложению. Настоятельно рекомендуется | Используйте AAC ViewModels для обработки бизнес-логики и получения данных приложения, чтобы представить состояние пользовательского интерфейса в пользовательском интерфейсе (Compose или представления Android). Дополнительные рекомендации по ViewModel см. здесь. Ознакомьтесь с преимуществами ViewModels здесь. |
Используйте коллекцию состояний пользовательского интерфейса с учетом жизненного цикла. Настоятельно рекомендуется | Соберите состояние пользовательского интерфейса из пользовательского интерфейса с помощью соответствующего построителя сопрограмм, учитывающего жизненный цикл: repeatOnLifecycle в системе View и collectAsStateWithLifecycle в Jetpack Compose. Узнайте больше о Узнайте больше о |
Не отправляйте события из ViewModel в пользовательский интерфейс. Настоятельно рекомендуется | Немедленно обработайте событие в ViewModel и вызовите обновление состояния с результатом обработки события. Подробнее о событиях пользовательского интерфейса здесь . |
Используйте приложение с одним действием. Рекомендуется | Используйте фрагменты навигации или компоновку навигации для навигации между экранами и создания глубоких ссылок на ваше приложение, если ваше приложение имеет более одного экрана. |
Используйте Jetpack Compose . Рекомендуется | Используйте Jetpack Compose для создания новых приложений для телефонов, планшетов, складных устройств и Wear OS. |
В следующем фрагменте показано, как собирать состояние пользовательского интерфейса с учетом жизненного цикла:
Просмотры
class MyFragment : Fragment() {
private val viewModel: MyViewModel by viewModel()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect {
// Process item
}
}
}
}
}
Сочинять
@Composable
fun MyScreen(
viewModel: MyViewModel = viewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
}
Модель просмотра
Модели представления отвечают за предоставление состояния пользовательского интерфейса и доступ к уровню данных. Вот несколько лучших практик для ViewModels:
Рекомендация | Описание |
---|---|
Модели ViewModels не должны зависеть от жизненного цикла Android. Настоятельно рекомендуется | Модели ViewModels не должны содержать ссылки на какой-либо тип, связанный с жизненным циклом. Не передавайте Activity, Fragment, Context или Resources в качестве зависимости. Если чему-то нужен Context в ViewModel, вам следует тщательно оценить, находится ли он на правильном уровне. |
Используйте сопрограммы и потоки . Настоятельно рекомендуется | ViewModel взаимодействует со слоями данных или домена, используя:
|
Используйте ViewModels на уровне экрана. Настоятельно рекомендуется | Не используйте ViewModels в повторно используемых частях пользовательского интерфейса. Вы должны использовать ViewModels в:
|
Используйте классы-держатели простого состояния в повторно используемых компонентах пользовательского интерфейса. Настоятельно рекомендуется | Используйте классы-держатели простого состояния для решения сложных задач в повторно используемых компонентах пользовательского интерфейса. Поступая таким образом, государство можно поднять и контролировать извне. |
Не используйте AndroidViewModel . Рекомендуется | Используйте класс ViewModel , а не AndroidViewModel . Класс Application не следует использовать в ViewModel. Вместо этого переместите зависимость в пользовательский интерфейс или на уровень данных. |
Раскройте состояние пользовательского интерфейса. Рекомендуется | Модели ViewModels должны предоставлять данные пользовательскому интерфейсу через одно свойство, называемое uiState . Если пользовательский интерфейс отображает несколько несвязанных фрагментов данных, виртуальная машина может предоставить несколько свойств состояния пользовательского интерфейса .
|
В следующем фрагменте показано, как предоставить состояние пользовательского интерфейса из 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
)
// ...
}
Жизненный цикл
Ниже приведены некоторые рекомендации по работе с жизненным циклом Android :
Рекомендация | Описание |
---|---|
Не переопределяйте методы жизненного цикла в действиях или фрагментах. Настоятельно рекомендуется | Не переопределяйте методы жизненного цикла, такие как onResume в действиях или фрагментах. Вместо этого используйте LifecycleObserver . Если приложению необходимо выполнить работу, когда жизненный цикл достигает определенного Lifecycle.State , используйте API repeatOnLifecycle . |
В следующем фрагменте показано, как выполнять операции в определенном состоянии жизненного цикла:
Просмотры
class MyFragment: Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {
override fun onResume(owner: LifecycleOwner) {
// ...
}
override fun onPause(owner: LifecycleOwner) {
// ...
}
}
}
}
Сочинять
@Composable
fun MyApp() {
val lifecycleOwner = LocalLifecycleOwner.current
DisposableEffect(lifecycleOwner, ...) {
val lifecycleObserver = object : DefaultLifecycleObserver {
override fun onStop(owner: LifecycleOwner) {
// ...
}
}
lifecycleOwner.lifecycle.addObserver(lifecycleObserver)
onDispose {
lifecycleOwner.lifecycle.removeObserver(lifecycleObserver)
}
}
}
Обработка зависимостей
Существует несколько рекомендаций, которые следует соблюдать при управлении зависимостями между компонентами:
Рекомендация | Описание |
---|---|
Используйте внедрение зависимостей . Настоятельно рекомендуется | Используйте лучшие практики внедрения зависимостей , в основном внедрение конструктора, когда это возможно. |
При необходимости перейдите к компоненту. Настоятельно рекомендуется | Область действия — контейнер зависимостей , если тип содержит изменяемые данные, которые необходимо совместно использовать, или тип требует больших затрат на инициализацию и широко используется в приложении. |
Используйте Хилт . Рекомендуется | Используйте Hilt или ручное внедрение зависимостей в простых приложениях. Используйте Hilt , если ваш проект достаточно сложен. Например, если у вас есть:
|
Тестирование
Ниже приведены некоторые рекомендации по тестированию :
Рекомендация | Описание |
---|---|
Знайте, что тестировать . Настоятельно рекомендуется | Если проект не такой же простой, как приложение hello world, вам следует протестировать его, как минимум, с помощью:
|
Предпочитайте подделки издевательствам. Настоятельно рекомендуется | Подробнее читайте в разделе «Использование тестовых двойников» в документации Android . |
Тестирование StateFlows. Настоятельно рекомендуется | При тестировании StateFlow :
|
Для получения дополнительной информации ознакомьтесь с руководством «Что тестировать в Android DAC» .
Модели
При разработке моделей в ваших приложениях следует соблюдать следующие рекомендации:
Рекомендация | Описание |
---|---|
Создавайте модели для каждого слоя в сложных приложениях. Рекомендуется | В сложных приложениях создавайте новые модели в разных слоях или компонентах, когда это имеет смысл. Рассмотрим следующие примеры:
|
Соглашения об именах
При именовании своей базы кода вы должны учитывать следующие рекомендации:
Рекомендация | Описание |
---|---|
Методы именования. Необязательный | Методы должны быть глагольной группой. Например, makePayment() . |
Именование свойств. Необязательный | Свойства должны быть существительными. Например, inProgressTopicSelection . |
Именование потоков данных. Необязательный | Когда класс предоставляет поток Flow, LiveData или любой другой поток, соглашение об именах — get{model}Stream() . Например, getAuthorStream(): Flow<Author> Если функция возвращает список моделей, имя модели должно быть во множественном числе: getAuthorsStream(): Flow<List<Author>> |
Именование реализаций интерфейсов. Необязательный | Имена реализаций интерфейсов должны быть осмысленными. Используйте префикс Default , если лучшее имя не найдено. Например, для интерфейса NewsRepository у вас может быть OfflineFirstNewsRepository или InMemoryNewsRepository . Если вы не можете найти хорошее имя, используйте DefaultNewsRepository . Поддельные реализации должны иметь префикс Fake , как в FakeAuthorsRepository . |