Na tej stronie znajdziesz kilka sprawdzonych metod i zaleceń dotyczących architektury. Stosuj je, aby poprawić jakość, niezawodność i skalowalność aplikacji. Ułatwiają też utrzymanie i testowanie aplikacji.
Poniższe sprawdzone metody są pogrupowane według tematu. Każdy z nich ma priorytet, który odzwierciedla, jak bardzo zespół go zaleca. Lista priorytetów:
- Zalecane: należy wdrożyć tę metodę, chyba że jest ona sprzeczna z Twoim podejściem.
- Zalecane: ta metoda prawdopodobnie poprawi jakość aplikacji.
- Opcjonalnie: w pewnych okolicznościach ta praktyka może poprawić działanie aplikacji.
Architektura warstwowa
Nasza zalecana architektura warstwowa preferuje dzielenie potencjalnych problemów. Interfejs użytkownika jest generowany na podstawie modeli danych, jest zgodny z zasadą pojedynczego źródła informacji oraz przestrzega zasad jednokierunkowego przepływu danych. Oto kilka sprawdzonych metod w przypadku architektury warstwowej:
Rekomendacja | Opis |
---|---|
Używaj wyraźnie zdefiniowanej warstwy danych.
Zdecydowanie zalecane |
Warstwa danych udostępnia dane aplikacji reszcie aplikacji i zawiera zdecydowaną większość logiki biznesowej aplikacji.
|
Używaj wyraźnie zdefiniowanej warstwy interfejsu użytkownika.
Zdecydowanie zalecane |
Warstwa UI wyświetla na ekranie dane aplikacji i służy jako główny punkt interakcji z użytkownikiem.
|
Warstwa danych powinna udostępniać dane aplikacji za pomocą repozytorium.
Zdecydowanie zalecane |
Komponenty w warstwie interfejsu, takie jak obiekty kompozycyjne, aktywności czy ViewModels, nie powinny wchodzić w bezpośrednią interakcję ze źródłem danych. Przykłady źródeł danych:
|
Używaj korobon i przepływów.
Zdecydowanie zalecane |
Do komunikacji między warstwami używaj korobon i przepływów. |
Użyj warstwy domeny.
Zalecane w dużych aplikacjach |
Użyj warstwy domeny, jeśli chcesz ponownie użyć logiki biznesowej, która współdziała z warstwą danych w różnych widokach ViewModel, lub jeśli chcesz uprościć złożoność logiki biznesowej w konkretnym widoku ViewModel. |
Warstwa interfejsu
Rola warstwy interfejsu polega na wyświetlaniu danych aplikacji na ekranie i służeniu za główny punkt interakcji użytkownika. Oto kilka sprawdzonych metod dotyczących warstwy UI:
Rekomendacja | Opis |
---|---|
Postępuj zgodnie z jednokierunkowym przepływem danych (UDF).
Zdecydowanie zalecane |
Stosuj zasady jednokierunkowego przepływu danych (UDF), w których ViewModels udostępnia stan interfejsu użytkownika za pomocą wzorca obserwatora i odbiera działania z interfejsu użytkownika przez wywołania metod. |
Użyj modeli ViewModel w AAC, jeśli ich zalety pasują do Twojej aplikacji.
Zdecydowanie zalecane |
Użyj ViewModels AAC, aby obsługiwać logikę biznesową, i pobierz dane aplikacji, aby ujawnić stan interfejsu użytkownika (Compose lub widoki Androida).
Więcej sprawdzonych metod dotyczących ViewModel znajdziesz tutaj. |
Używaj kolekcji stanu interfejsu uwzględniającej cykl życia.
Zdecydowanie zalecane |
Zbieraj stan interfejsu z interfejsu za pomocą odpowiedniego kreatora coroutine uwzględniającego cykl życia: repeatOnLifecycle w systemie View i collectAsStateWithLifecycle w Jetpack Compose.
Dowiedz się więcej o Dowiedz się więcej na ten temat: |
Nie wysyłaj zdarzeń z ViewModel do interfejsu użytkownika.
Zdecydowanie zalecane |
Przetwórz zdarzenie natychmiast w modelu ViewModel i wywołaj aktualizację stanu w wyniku obsługi zdarzenia. Więcej informacji o zdarzeniach interfejsu użytkownika. |
Użyj aplikacji z jednym działaniem.
Zalecane |
Jeśli Twoja aplikacja ma więcej niż 1 ekran, do poruszania się między ekranami i precyzyjnych linków do aplikacji używaj fragmentów nawigacji lub kompozycji nawigacyjnych. |
Użyj Jetpack Compose.
Zalecane |
Za pomocą Jetpack Compose możesz tworzyć nowe aplikacje na telefony, tablety, urządzenia składane i Wear OS. |
Ten fragment kodu pokazuje, jak pobierać stan interfejsu użytkownika z uwzględnieniem cyklu życia:
Wyświetlenia
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
}
}
}
}
}
Compose
@Composable
fun MyScreen(
viewModel: MyViewModel = viewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
}
ViewModel
Obiekty ViewModels odpowiadają za udostępnianie stanu interfejsu użytkownika i dostępu do warstwy danych. Oto kilka sprawdzonych metod dotyczących widoków modelu:
Rekomendacja | Opis |
---|---|
Modele widoku danych powinny być niezależne od cyklu życia Androida.
Zdecydowanie zalecane |
Obiekty ViewModels nie powinny zawierać odwołania do żadnego typu powiązanego z cyklem życia. Nie przekazuj wartości Activity, Fragment, Context ani Resources jako zależności.
Jeśli coś wymaga Context w modelu widoku, musisz zdecydowanie sprawdzić, czy znajduje się on we właściwej warstwie. |
Używaj korobon i przepływów.
Zdecydowanie zalecane |
Model widoku danych współdziała z warstwami danych lub domeny za pomocą:
|
Używaj widoków ViewModel na poziomie ekranu.
Zdecydowanie zalecane |
Nie używaj widoków w elementach interfejsu użytkownika, które można ponownie wykorzystać. ViewModels należy używać w tych sytuacjach:
|
W komponentach UI do wielokrotnego użytku używaj zwykłych klas uchwytów stanu.
Zdecydowanie zalecane |
Aby poradzić sobie z zaawansowanymi funkcjami w wielokrotnie używanych komponentach UI, użyj zwykłych klas przechowujących stan. Dzięki temu stan może być podnoszony i kontrolowany zewnętrznie. |
Nie używaj AndroidViewModel .
Zalecane |
Użyj klasy ViewModel , a nie AndroidViewModel . Klasy Application nie należy używać w klasie ViewModel. Zamiast tego przenieś zależność do interfejsu użytkownika lub warstwy danych. |
udostępniać stan interfejsu,
Zalecane |
Modele widoku powinny udostępniać dane interfejsowi za pomocą jednej właściwości o nazwie uiState . Jeśli interfejs użytkownika wyświetla wiele niezwiązanych ze sobą elementów danych, maszyna wirtualna może wyświetlać wiele właściwości stanu interfejsu użytkownika.
|
Ten fragment kodu pokazuje, jak ujawnić stan UI z interfejsu 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
)
// ...
}
Cykl życia
Oto kilka sprawdzonych metod dotyczących cyklu życia aplikacji na Androida:
Rekomendacja | Opis |
---|---|
Nie zastępuj metod cyklu życia w aktywnościach ani fragmentach.
Zdecydowanie zalecane |
Nie zastępuj metod cyklu życia, takich jak onResume , w działaniach lub fragmentach. Zamiast niego użyj LifecycleObserver . Jeśli aplikacja musi wykonać zadanie, gdy cykl życia osiągnie określony Lifecycle.State , użyj interfejsu API repeatOnLifecycle . |
W tym fragmencie kodu opisano, jak wykonywać operacje w zależności od stanu cyklu życia:
Wyświetlenia
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) {
// ...
}
}
}
}
Compose
@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)
}
}
}
Zarządzanie zależnościami
Jest kilka sprawdzonych metod, o których warto pamiętać, zarządzając zależnościami między komponentami:
Rekomendacja | Opis |
---|---|
Używaj wstrzykiwania zależności.
Zdecydowanie zalecane |
Stosuj sprawdzone metody korzystania z wstrzykiwania zależności, głównie wstrzykiwania konstruktora, gdy to możliwe. |
W razie potrzeby ogranicz zakres do komponentu.
Zdecydowanie zalecane |
Ogranicz dostęp do kontenera zależności, gdy typ zawiera dane, które można zmienić i które trzeba udostępnić, lub gdy typ jest kosztowny w inicjalizacji i jest często używany w aplikacji. |
Użyj Hilt.
Zalecane |
W prostych aplikacjach używaj Hilt lub ręcznego wstrzykiwania zależności. Użyj Hilt, jeśli Twój projekt jest na tyle złożony. Jeśli na przykład masz:
|
Testowanie
Oto kilka sprawdzonych metod dotyczących testowania:
Rekomendacja | Opis |
---|---|
Co warto przetestować.
Zdecydowanie zalecane |
Jeśli projekt nie jest tak prosty jak aplikacja „hello world”, należy go przetestować, używając co najmniej:
|
preferować fałszywe treści zamiast mockupów.
Zdecydowanie zalecane |
Więcej informacji znajdziesz w dokumentacji Androida na temat używania podwójnych testów. |
Testowanie StateFlow.
Zdecydowanie zalecane |
Podczas testowania StateFlow :
|
Więcej informacji znajdziesz w przewodniku DAC na temat Androida.
Modele
Podczas tworzenia modeli w aplikacjach należy przestrzegać tych sprawdzonych metod:
Rekomendacja | Opis |
---|---|
tworzenie modelu na warstwę w skomplikowanych aplikacjach.
Zalecane |
W skomplikowanych aplikacjach, gdy ma to sens, twórz nowe modele na różnych warstwach lub komponentach. Zapoznaj się z tymi przykładami:
|
Konwencje nazewnictwa
Podczas nadawania nazwy kodowi źródłowemu pamiętaj o tych sprawdzonych metodach:
Rekomendacja | Opis |
---|---|
Nazwy metod.
Opcjonalnie |
Metody powinny być wyrażeniem czasownikowym. Na przykład: makePayment() . |
Nazywanie właściwości.
Opcjonalny |
Właściwości powinny być wyrażeniami rzeczownikowymi. Na przykład: inProgressTopicSelection . |
Nazywanie strumieni danych.
Opcjonalnie |
Gdy klasa ujawnia strumień Flow, LiveData lub dowolny inny strumień, konwencja nazewnictwa jest zgodna z konwencją get{model}Stream() . Na przykład getAuthorStream(): Flow<Author> Jeśli funkcja zwraca listę modeli, nazwa modelu powinna być w liczbie mnogiej: getAuthorsStream(): Flow<List<Author>> |
Implementacje interfejsów nazewnictwa.
Opcjonalnie |
Nazwy implementacji interfejsów powinny być zrozumiałe. Jeśli nie możesz znaleźć lepszej nazwy, użyj prefiksu Default . Na przykład w przypadku interfejsu NewsRepository możesz mieć OfflineFirstNewsRepository lub InMemoryNewsRepository . Jeśli nie możesz znaleźć odpowiedniej nazwy, użyj DefaultNewsRepository .
Fałszywe implementacje powinny mieć prefiks Fake , np. FakeAuthorsRepository . |