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 . |