Na tej stronie znajdziesz kilka sprawdzonych metod i rekomendacji dotyczących architektury. Zastosuj je, aby poprawić jakość, niezawodność i skalowalność aplikacji. Ułatwiają też jej obsługę i testowanie.
Poniższe sprawdzone metody są uporządkowane według tematów. Każdy z nich ma priorytet, który odzwierciedla, jak mocno go poleca zespół. Oto lista priorytetów:
- Zdecydowanie zalecane: zastosuj tę praktykę, chyba że koliduje ona z Twoim podejściem.
- Zalecane: ta praktyka może poprawić Twoją aplikację.
- Opcjonalnie: może to pomóc w ulepszaniu aplikacji w określonych okolicznościach.
Architektura warstwowa
Nasza rekomendowana przez nas architektura warstwowa preferuje rozdzielenie potencjalnych problemów. Wykorzystuje on modele danych dla interfejsu użytkownika, jest zgodny z zasadą dotyczącą jednego źródła danych i działa zgodnie z zasadami jednokierunkowego przepływu danych. Oto kilka sprawdzonych metod tworzenia architektury warstwowej:
Rekomendacja | Opis |
---|---|
Użyj wyraźnie zdefiniowanej warstwy danych.
Zdecydowanie zalecane |
Warstwa danych ujawnia dane aplikacji pozostałym jej członkom i przechowuje większość logiki biznesowej aplikacji.
|
Używaj wyraźnie zdefiniowanej warstwy interfejsu.
Zdecydowanie zalecane |
Warstwa interfejsu wyświetla dane aplikacji na ekranie i służy za główny punkt interakcji użytkownika.
|
Warstwa danych powinna udostępniać dane aplikacji za pomocą repozytorium.
Zdecydowanie zalecane |
Komponenty warstwy interfejsu, takie jak obiekty kompozycyjne, aktywności czy obiekty typu ViewModels, nie powinny wchodzić w bezpośrednie interakcje ze źródłem danych. Przykłady źródeł danych:
|
Użyj korekty i przepływów.
Zdecydowanie zalecane |
Do komunikacji między warstwami używaj współpracy i przepływów. |
Użyj warstwy domeny.
Polecane w dużych aplikacjach |
Użyj warstwy domeny, jeśli chcesz ponownie wykorzystać logikę biznesową, która współdziała z warstwą danych w wielu modelach widoków danych, lub chcesz uprościć złożoność logiki biznesowej konkretnego modelu. |
Warstwa interfejsu
Rola warstwy interfejsu to wyświetlanie danych aplikacji na ekranie i stanowienie głównego punktu interakcji użytkownika. Oto kilka sprawdzonych metod dotyczących warstwy UI:
Rekomendacja | Opis |
---|---|
Postępuj zgodnie z jednokierunkowym przepływem danych (UDF).
Zdecydowanie zalecane |
Postępuj zgodnie z zasadami niekierunkowego przepływu danych (UDF), gdzie modele View Modele ujawniają stan interfejsu użytkownika za pomocą wzorca obserwatora i otrzymują działania z interfejsu za pomocą wywołań metod. |
Używaj modeli widoków AAC, jeśli ich zalety dotyczą Twojej aplikacji.
Zdecydowanie zalecane |
Modele ViewModels AAC służą do obsługi logiki biznesowej i pobierania danych aplikacji w celu ujawniania stanu UI w interfejsie (widoku tworzenia lub widoków Androida).
Więcej sprawdzonych metod korzystania z ViewModel znajdziesz tutaj. Poznaj zalety modeli ViewModels. |
Używaj zbierania informacji o stanie interfejsu z uwzględnieniem cyklu życia.
Zdecydowanie zalecane |
Zbieraj informacje o stanie interfejsu z poziomu interfejsu za pomocą odpowiedniego konstruktora sterowników identyfikujących cykl życia: repeatOnLifecycle w systemie Widok i collectAsStateWithLifecycle w Jetpack Compose.
Dowiedz się więcej o Dowiedz się więcej o |
Nie wysyłaj zdarzeń z modelu ViewModel do interfejsu użytkownika.
Zdecydowanie zalecane |
Natychmiast przetwórz zdarzenie w modelu ViewModel i wywołaj aktualizację stanu w wyniku obsługi tego zdarzenia. Więcej informacji o zdarzeniach związanych z interfejsem użytkownika |
używać aplikacji mającej pojedynczą aktywność;
Zalecane |
Używaj funkcji Fragmenty nawigacyjne lub Tworzenie wiadomości w nawigacji, aby przechodzić między ekranami, a jeśli aplikacja ma więcej niż 1 ekran, korzystaj z precyzyjnego linku do aplikacji. |
korzystać z usługi Jetpack Compose,
Zalecane |
Korzystaj z usługi Jetpack Compose, aby tworzyć nowe aplikacje na telefony, tablety, urządzenia składane i Wear OS. |
Ten fragment kodu pokazuje, jak rejestrować stan interfejsu użytkownika w sposób odzwierciedlający cykl ż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
}
}
}
}
}
Utwórz
@Composable
fun MyScreen(
viewModel: MyViewModel = viewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
}
ViewModel
Obiekty ViewModels odpowiadają za określanie stanu interfejsu użytkownika i dostęp do warstwy danych. Oto kilka sprawdzonych metod dotyczących modeli widoków:
Rekomendacja | Opis |
---|---|
Modele ViewModel powinny być niezależne od cyklu życia Androida.
Zdecydowanie zalecane |
Modele ViewModel nie powinny zawierać odwołań do żadnych typów związanych z cyklem życia. Nie przekazuj jako zależności Activity, Fragment, Context ani Resources .
Jeśli coś wymaga parametru Context w modelu ViewModel, zdecydowanie zalecamy sprawdzenie, czy znajduje się on w odpowiedniej warstwie. |
Użyj korekty i przepływów.
Zdecydowanie zalecane |
Model ViewModel wchodzi w interakcję z warstwami danych lub domen za pomocą:
|
Używaj modeli widoków na poziomie ekranu.
Zdecydowanie zalecane |
Nie używaj obiektów ViewModel w elementach interfejsu wielokrotnego użytku. Modeli View należy używać w:
|
W komponentach interfejsu wielokrotnego użytku używaj klas zastępujących w zwykłym stanie.
Zdecydowanie zalecane |
Do obsługi złożoności komponentów interfejsu wielokrotnego użytku używaj klas posiadaczy zwykłego stanu. W ten sposób władze mogą przekazywać władze na zewnątrz i nimi zarządzać. |
Nie używaj AndroidViewModel .
Zalecane |
Użyj zajęć ViewModel , a nie AndroidViewModel . Klasa Application nie powinna być używana w obiekcie ViewModel. Zamiast tego przenieś zależność do interfejsu użytkownika lub warstwy danych. |
Ujawniaj stan interfejsu użytkownika.
Zalecane |
Obiekty ViewModele powinny udostępniać dane w interfejsie za pomocą pojedynczej właściwości o nazwie uiState . Jeśli interfejs użytkownika wyświetla wiele niepowiązanych ze sobą danych, maszyna wirtualna może wydzielić wiele właściwości stanu interfejsu.
|
Ten fragment kodu pokazuje, jak udostępnić stan interfejsu użytkownika w modelu 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 korzystania z cyklu życia Androida:
Rekomendacja | Opis |
---|---|
Nie zastępuj metod cyklu życia w działaniach ani we fragmentach.
Zdecydowanie zalecane |
Nie zastępuj metod cyklu życia, takich jak onResume w działaniach czy fragmentach kodu. Użyj w zamian LifecycleObserver . Jeśli aplikacja musi działać, gdy cykl życia osiągnie określoną wartość Lifecycle.State , użyj interfejsu API repeatOnLifecycle . |
Ten fragment kodu pokazuje, jak wykonywać operacje w określonym stanie 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) {
// ...
}
}
}
}
Utwórz
@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)
}
}
}
Obsługa zależności
Poniżej znajdziesz sprawdzone metody zarządzania zależnościami między komponentami:
Rekomendacja | Opis |
---|---|
Użyj funkcji wstrzykiwania zależności.
Zdecydowanie zalecane |
W miarę możliwości korzystaj ze sprawdzonych metod dotyczących wstrzykiwania zależności, głównie z wykorzystaniem wstrzykiwania konstruktora. |
W razie potrzeby ustaw zakres na komponent.
Zdecydowanie zalecane |
Ustaw zakres na kontener zależności, jeśli typ zawiera zmienne, które należy udostępnić, lub ten typ jest drogi do zainicjowania i jest powszechnie używany w aplikacji. |
Użyj narzędzia Hilt.
Zalecane |
Użyj funkcji Hilt lub ręcznego wstrzykiwania zależności w prostych aplikacjach. Jeśli Twój projekt jest wystarczająco złożony, użyj opcji Hilt. Jeśli na przykład masz:
|
Testowanie
Oto kilka sprawdzonych metod testowania:
Rekomendacja | Opis |
---|---|
Dowiedz się, co testować.
Zdecydowanie zalecane |
Jeśli projekt nie jest mniej więcej tak prosty jak aplikacja Hello World, należy go przetestować przynajmniej przy użyciu:
|
Woli podszyć się po drugiej stronie.
Zdecydowanie zalecane |
Więcej informacji znajdziesz w artykule Używanie podwójnej precyzji w testach w dokumentacji Androida. |
Testuj StateFlows.
Zdecydowanie zalecane |
Podczas testowania StateFlow :
|
Więcej informacji znajdziesz w przewodniku po testowaniu urządzeń z Androidem (w języku angielskim).
Modele
Podczas tworzenia modeli w aplikacjach należy stosować się do tych sprawdzonych metod:
Rekomendacja | Opis |
---|---|
Tworzenie modelu na warstwę w złożonych aplikacjach.
Zalecane |
W złożonych aplikacjach twórz nowe modele w różnych warstwach lub komponentach, jeśli ma to sens. Przeanalizuj te przykłady:
|
Konwencje nazewnictwa
Podczas nazywania bazy kodu pamiętaj o tych sprawdzonych metodach:
Rekomendacja | Opis |
---|---|
Metody nazewnictwa.
Opcjonalny |
Metody powinny być wyrażeniami typu czasowniki. Przykład: makePayment() . |
Właściwości nazewnictwa.
Opcjonalny |
Właściwości powinny być wyrażeniem rzeczowym. Przykład: inProgressTopicSelection . |
Nadawanie nazw strumieniom danych.
Opcjonalny |
Gdy klasa udostępnia strumień przepływów, LiveData lub dowolnego innego strumienia, konwencja nazewnictwa to 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 nadawania nazw.
Opcjonalny |
Nazwy implementacji interfejsów powinny być zrozumiałe. Jeśli nie można znaleźć lepszej nazwy, zastosuj prefiks Default . Na przykład w przypadku interfejsu NewsRepository może to być OfflineFirstNewsRepository lub InMemoryNewsRepository . Jeśli nie możesz znaleźć dobrej nazwy, użyj DefaultNewsRepository .
Fałszywe implementacje powinny być poprzedzone prefiksem Fake , np. FakeAuthorsRepository . |