Zalecenia dotyczące architektury Androida

Na tej stronie znajdziesz kilka sprawdzonych metod i zaleceń dotyczących architektury. Zastosuj je, aby poprawić jakość, niezawodność i skalowalność swojej aplikacji. Poza tym ułatwiają obsługę i testowanie aplikacji.

Sprawdzone metody wymienione poniżej są pogrupowane według tematów. Każdy z nich ma priorytet, który odzwierciedla jak zdecydowanie jest to zalecane przez zespół. Oto lista priorytetów:

  • Zdecydowanie zalecane: zalecamy wdrożenie tej metody, chyba że sprzeczne ze sobą co jest zgodne z Pana/Pani firmą.
  • Zalecane: ta metoda prawdopodobnie poprawi Twoją aplikację.
  • Opcjonalnie: w pewnych okolicznościach ta praktyka może poprawić działanie aplikacji.
.

Warstwowa architektura

Nasza zalecana architektura warstwowa preferuje dzielenie potencjalnych problemów. it Napędza UI z modeli danych, jest zgodny z zasadą jednego źródła danych, i jest zgodny z zasadami jednokierunkowego przepływu danych. Oto kilka najlepszych w przypadku architektury warstwowej:

Rekomendacja Opis
Użyj jasno określonej warstwy danych.
Zdecydowanie zalecane
Warstwa danych ujawnia dane aplikacji reszty aplikacji i zawiera zdecydowaną większość logiki biznesowej aplikacji.
  • Należy tworzyć repozytoria, nawet jeśli zawierają one tylko jedno źródło danych.
  • W przypadku małych aplikacji typy warstw danych możesz umieścić w pakiecie lub module data.
Użyj jasno określonej warstwy interfejsu.
Zdecydowanie zalecane
Warstwa interfejsu wyświetla na ekranie dane aplikacji i stanowi główny punkt interakcji użytkownika.
  • W przypadku małych aplikacji typy warstw danych możesz umieścić w pakiecie lub module ui.
. Więcej sprawdzonych metod dotyczących warstwy interfejsu
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:

  • Bazy danych, DataStore, SharedPreferences, Firebase API.
  • Dostawcy lokalizacji GPS.
  • Dostawcy danych Bluetooth.
  • Dostawca stanu połączenia sieciowego.
Używaj współrzędnych i przepływów.
Zdecydowanie zalecane
Do komunikacji między warstwami używaj współrzędnych i przepływów.

Więcej sprawdzonych metod dotyczących współprac znajdziesz tutaj

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 ViewModel, lub jeśli chcesz uprościć logikę biznesową danego modelu widoku.

warstwa interfejsu

Rola warstwy interfejsu polega na wyświetlaniu danych aplikacji na ekranie. i służą za główny punkt interakcji użytkownika. Oto kilka sprawdzonych metod dla warstwy interfejsu użytkownika:

Rekomendacja Opis
Wykonaj czynności opisane w artykule Niekierunkowy przepływ danych (UDF).
Zdecydowanie zalecane
Przestrzegaj zasad jednokierunkowego przepływu danych (UDF), w którym modele ViewModels ujawniają stan interfejsu użytkownika za pomocą wzorca obserwatora i odbierają działania z interfejsu za pomocą wywołań metod.
Używaj modelu AAC ViewModels, jeśli jego zalety mają zastosowanie do Twojej aplikacji.
Zdecydowanie zalecane
Modele widoków AAC służą do obsługi logiki biznesowej i do pobierania danych aplikacji w celu ujawnienia stanu interfejsu użytkownika (komunikacji lub widoków Androida).

Zobacz więcej sprawdzonych metod ViewModel.

Poznaj zalety modeli widoków.

Używaj kolekcji stanów interfejsu uwzględniającego cykl życia.
Zdecydowanie zalecane
Zbieraj informacje o stanie interfejsu z UI za pomocą odpowiedniego kreatora współpracowania dostosowanego do cyklu życia: repeatOnLifecycle w systemie wyświetlania i collectAsStateWithLifecycle w Jetpack Compose.

Dowiedz się więcej o repeatOnLifecycle.

Dowiedz się więcej na ten temat: collectAsStateWithLifecycle.

Nie wysyłaj zdarzeń z modelu 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 w interfejsie znajdziesz tutaj.
Użyj aplikacji składającej się z pojedynczej czynności.
Zalecane
Jeśli Twoja aplikacja ma więcej niż 1 ekran, do przechodzenia między ekranami i precyzyjnych linków do aplikacji używaj fragmentów nawigacji lub kompozycji nawigacyjnych.
korzystać z Jetpack Compose;
Zalecane
Za pomocą Jetpack Compose możesz tworzyć nowe aplikacje na telefony, tablety, urządzenia składane i Wear OS.

Poniższy fragment kodu opisuje, jak rejestrować stan UI w kontekście cyklu życia w sposób:

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()
}

Wyświetl model

Modele ViewModels odpowiadają za udostępnianie stanu interfejsu użytkownika i dostępu do funkcji warstwy danych. Oto kilka sprawdzonych metod dotyczących modeli widoków:

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 zasady 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 współrzędnych i przepływów.
Zdecydowanie zalecane

Model widoku danych współdziała z warstwami danych lub domeny za pomocą:

  • Przepływy Kotlin do odbierania danych aplikacji
  • Funkcje suspend do wykonywania działań przy użyciu viewModelScope.
Używaj modeli widoków na poziomie ekranu.
Zdecydowanie zalecane

Nie używaj modeli ViewModels w elementach interfejsu wielokrotnego użytku. Modeli ViewModel należy używać w tych obszarach:

  • funkcje kompozycyjne na poziomie ekranu,
  • Aktywności/fragmenty filmów w widokach,
  • Miejsca docelowe lub wykresy dostępne w Jetpack Navigation.
Używaj zwykłych klas obiektów stanowych w komponentach interfejsu wielokrotnego użytku.
Zdecydowanie zalecane
Używaj zwykłych klas obiektów stanu do obsługi złożoności komponentów interfejsu wielokrotnego użytku. Dzięki temu stan można podnosić i kontrolować z zewnątrz.
Nie używaj AndroidViewModel.
Zalecane
Używaj zajęć ViewModel, a nie AndroidViewModel. W modelu ViewModel nie należy używać klasy Application. Zamiast tego przenieś zależność do interfejsu użytkownika lub warstwy danych.
udostępniać stan interfejsu,
Zalecane
Modele widoków danych powinny udostępniać dane w interfejsie za pomocą jednej usługi o nazwie uiState. Jeśli interfejs użytkownika wyświetla wiele niepowiązanych ze sobą elementów danych, maszyna wirtualna może udostępniać wiele właściwości stanu interfejsu.
  • Zmień rolę uiState jako StateFlow.
  • Jeśli dane pochodzą z innych warstw hierarchii, utwórz uiState za pomocą operatora stateIn z zasadą WhileSubscribed(5000) (przykład).
  • W prostszych przypadkach, w których nie ma strumieni danych pochodzących z warstwy danych, akceptowalne jest użycie obiektu MutableStateFlow ujawnionego jako stałej wartości StateFlow (przykład).
  • Możesz ustawić ${Screen}UiState jako klasę danych, która może zawierać dane, błędy i sygnały wczytywania. Ta klasa może być również klasą zapieczętowaną, jeśli różne stany są wyłączne.

Ten fragment kodu opisuje, jak udostępnić stan interfejsu za pomocą 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 pracy z Androidem cykl życia:

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 działać, gdy cykl życia osiągnie określoną wartość Lifecycle.State, użyj interfejsu API repeatOnLifecycle.

Fragment kodu poniżej pokazuje, jak wykonywać operacje na określonych danych Stan 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)
        }
    }
}

Obsługa zależności

Jest kilka sprawdzonych metod, o których warto pamiętać, zarządzając zależnościami. między komponentami:

Rekomendacja Opis
Użyj wstrzykiwania zależności.
Zdecydowanie zalecane
Stosuj sprawdzone metody dotyczące wstrzykiwania zależności, głównie wstrzykiwania przez konstruktor, gdy to możliwe.
W razie potrzeby ustaw zakres na komponent.
Zdecydowanie zalecane
Ustaw zakres na kontener zależności, gdy typ zawiera zmienne dane, które należy udostępnić, lub gdy typ jest kosztowny w celu zainicjowania i jest powszechnie używany w aplikacji.
Użyj Hilt.
Zalecane
W prostych aplikacjach używaj Hilt lub ręcznego wstrzykiwania zależności. Jeśli Twój projekt jest wystarczająco złożony, użyj narzędzia Hilt. Jeśli na przykład masz:
  • Wiele ekranów z funkcją ViewModels – integracja
  • Wykorzystanie w usłudze WorkManager – integracja
  • Szybsze wykorzystanie Nawigacji, takie jak modele Viewmodele ograniczone do wykresu nawigacyjnego – integracja.

Testowanie

Poniżej znajdziesz sprawdzone metody testowania:

Rekomendacja Opis
Co warto przetestować.
Zdecydowanie zalecane

Jeśli projekt nie jest tak prosty jak aplikacja Hello World, warto ją przetestować. W tym celu:

  • Modele View Modele do testów jednostkowych, w tym przepływy.
  • Encje warstwy danych w teście jednostkowym. Chodzi o repozytoria i źródła danych.
  • Testy nawigacji w interfejsie, które są przydatne jako testy regresji w CI.
Wolę fałszerstwo niż psikusy.
Zdecydowanie zalecane
Więcej informacji znajdziesz w artykule o używaniu wartości deweloperskich w wersji testowej w dokumentacji Androida.
Przetestuj przepływy StateFlows.
Zdecydowanie zalecane
Podczas testowania aplikacji StateFlow:

Więcej informacji znajdziesz w przewodniku po funkcjach DAC na Androida.

Modele

Podczas tworzenia modeli w aplikacjach przestrzegaj tych sprawdzonych metod:

Rekomendacja Opis
Tworzenie modelu na warstwę w złożonych aplikacjach.
Zalecane

W złożonych aplikacjach w razie potrzeby twórz nowe modele w różnych warstwach lub komponentach. Zobacz te przykłady:

  • Zdalne źródło danych może zmapować model odbierany przez sieć na prostszą klasę z samymi danymi, których potrzebuje aplikacja
  • Repozytoria mogą mapować modele DAO na prostsze klasy danych z użyciem tylko tych informacji, których potrzebuje warstwa interfejsu.
  • ViewModel może uwzględniać modele warstwy danych w klasach UiState.

Konwencje nazewnictwa

Nazywając bazę kodu, pamiętaj o tych sprawdzonych metodach:

Rekomendacja Opis
Metody nazewnictwa.
Opcjonalny
Metody powinny być wyrażeniami czasownikowymi. Na przykład: makePayment().
Właściwości nazewnictwa.
Opcjonalny
Właściwości powinny być powiązane z funkcją rzeczownikową. Na przykład: inProgressTopicSelection.
Nazewnictwo strumieni danych.
Opcjonalny
Gdy klasa udostępnia 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 musi być w liczbie mnogiej: getAuthorsStream(): Flow<List<Author>>
Implementacje interfejsów nazewnictwa.
Opcjonalny
Nazwy implementacji interfejsów powinny być zrozumiałe. Jeśli nie można znaleźć lepszej nazwy, ustaw prefiks Default. W przypadku interfejsu NewsRepository można na przykład mieć wartość OfflineFirstNewsRepository lub InMemoryNewsRepository. Jeśli nie możesz znaleźć dobrego imienia, użyj DefaultNewsRepository. Fałszywe implementacje powinny być poprzedzone ciągiem Fake, np. FakeAuthorsRepository.