Na tej stronie znajdziesz kilka sprawdzonych metod i zaleceń dotyczących architektury. Wprowadź je, aby poprawić jakość, niezawodność i skalowalność aplikacji. Ułatwiają też utrzymywanie i testowanie aplikacji.
Poniższe sprawdzone metody są pogrupowane według tematu. Każda z nich ma priorytet, który odzwierciedla siłę rekomendacji. Lista priorytetów jest następująca:
- Zdecydowanie zalecane: wdróż tę metodę, chyba że jest ona sprzeczna z Twoim podejściem.
- Zalecane: ta praktyka prawdopodobnie poprawi jakość Twojej aplikacji.
- Opcjonalnie: w określonych okolicznościach może to poprawić jakość aplikacji.
Architektura warstwowa
Zalecana przez nas architektura warstwowa sprzyja rozdzieleniu zadań. Interfejs użytkownika jest tworzony na podstawie modeli danych, jest zgodny z zasadą jednego źródła wiarygodnych informacji i z zasadami jednokierunkowego przepływu danych. Oto kilka sprawdzonych metod dotyczących architektury warstwowej:
| Rekomendacja | Opis |
|---|---|
| Używaj jasno określonej warstwy danych.
Zdecydowanie zalecane |
Warstwa danych udostępnia dane aplikacji pozostałym jej częściom i zawiera 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 jako główny punkt interakcji użytkownika. Jetpack Compose to zalecany nowoczesny zestaw narzędzi do tworzenia interfejsu aplikacji.
|
| Udostępnij dane aplikacji z warstwy danych za pomocą repozytorium.
Zdecydowanie zalecane |
Upewnij się, że komponenty w warstwie interfejsu, takie jak funkcje kompozycyjne czy modele ViewModel, nie wchodzą w bezpośrednią interakcję ze źródłem danych. Przykłady źródeł danych:
|
| Używaj współprogramów i przepływów.
Zdecydowanie zalecane |
Do komunikacji między warstwami używaj korutyn i przepływów.
Więcej informacji o sprawdzonych metodach dotyczących współprogramów znajdziesz w artykule Sprawdzone metody dotyczące współprogramów na Androidzie. |
| Użyj warstwy domeny.
Rekomendacje w popularnych aplikacjach |
Używaj warstwy domeny w przypadku przypadków użycia, w których musisz ponownie wykorzystać logikę biznesową, która wchodzi w interakcję z warstwą danych w wielu modelach ViewModel, lub gdy chcesz uprościć złożoność logiki biznesowej konkretnego modelu ViewModel. |
Warstwa interfejsu
Warstwa interfejsu służy do wyświetlania danych aplikacji na ekranie i jest głównym punktem interakcji użytkownika. Oto kilka sprawdzonych metod dotyczących warstwy interfejsu:
| Rekomendacja | Opis |
|---|---|
| Postępuj zgodnie z zasadami jednokierunkowego przepływu danych (UDF).
Zdecydowanie zalecane |
Postępuj zgodnie z zasadami jednokierunkowego przepływu danych (UDF), w którym obiekty ViewModel udostępniają stan interfejsu za pomocą wzorca obserwatora i otrzymują działania z interfejsu za pomocą wywołań metod. |
| Używaj AAC ViewModels, jeśli ich zalety mają zastosowanie w Twojej aplikacji.
Zdecydowanie zalecane |
Używaj AAC ViewModels do obsługi logiki biznesowej i pobierania danych aplikacji, aby udostępniać interfejsowi stan interfejsu.
Więcej informacji o sprawdzonych metodach dotyczących ViewModel znajdziesz w artykule Zalecenia dotyczące architektury. Więcej informacji o zaletach ViewModels znajdziesz w artykule ViewModel jako kontener stanu logiki biznesowej. |
| Używaj zbierania stanu interfejsu opartego na cyklu życia.
Zdecydowanie zalecane |
Zbieraj stan interfejsu z interfejsu za pomocą odpowiedniego konstruktora współprogramów uwzględniającego cykl życia, collectAsStateWithLifecycle.
Więcej informacji o |
| Nie wysyłaj zdarzeń z ViewModel do interfejsu.
Zdecydowanie zalecane |
Natychmiast przetwórz zdarzenie w obiekcie ViewModel i spowoduj aktualizację stanu z wynikiem obsługi zdarzenia. Więcej informacji o zdarzeniach interfejsu znajdziesz w artykule Obsługa zdarzeń ViewModel. |
| Używaj aplikacji z jedną aktywnością.
Zdecydowanie zalecane |
Użyj komponentu Navigation 3, aby przechodzić między ekranami i precyzyjnie linkować do aplikacji, jeśli ma ona więcej niż 1 ekran. |
| Używaj Jetpack Compose.
Zdecydowanie zalecane |
Używaj Jetpack Compose do tworzenia nowych aplikacji na telefony, tablety, urządzenia składane i zegarki z Wear OS. |
Poniższy fragment kodu pokazuje, jak zbierać stan interfejsu w sposób uwzględniający cykl życia:
@Composable
fun MyScreen(
viewModel: MyViewModel = viewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
}
ViewModel
ViewModele odpowiadają za udostępnianie stanu interfejsu i dostęp do warstwy danych. Oto kilka sprawdzonych metod dotyczących klas ViewModel:
| Rekomendacja | Opis |
|---|---|
| Zachowaj niezależność obiektów ViewModel od cyklu życia Androida.
Zdecydowanie zalecane |
W klasach ViewModel nie przechowuj odwołań do żadnych typów związanych z cyklem życia. Nie przekazuj Activity, Context ani Resources jako zależności.
Jeśli coś wymaga Context w ViewModelu, dokładnie sprawdź, czy jest to odpowiednia warstwa. |
| Używaj współprogramów i przepływów.
Zdecydowanie zalecane |
ViewModel wchodzi w interakcję z warstwami danych lub domen za pomocą tych elementów:
|
| Używaj obiektów ViewModel na poziomie ekranu.
Zdecydowanie zalecane |
Nie używaj klas ViewModel w elementach interfejsu, których można używać wielokrotnie. Modeli ViewModel należy używać w tych przypadkach:
W przypadku bardziej złożonych funkcji kompozycyjnych lub tych, których działanie dynamicznie zmienia się w zależności od stanu, użyj |
| Używaj zwykłych klas przechowujących stan w komponentach interfejsu do wielokrotnego użytku.
Zdecydowanie zalecane |
Używaj zwykłych klas przechowujących stan do obsługi złożoności w komponentach interfejsu do wielokrotnego użytku. W takim przypadku stan może zostać przeniesiony i sterowany zewnętrznie. |
Nie używaj AndroidViewModel.
Wybrane dla Ciebie |
Użyj klasy ViewModel, a nie AndroidViewModel. Nie używaj klasy Application w obiekcie ViewModel. Zamiast tego przenieś zależność do interfejsu lub warstwy danych. |
| Udostępnij stan interfejsu.
Wybrane dla Ciebie |
Spraw, aby obiekty ViewModel udostępniały dane interfejsowi za pomocą jednej właściwości o nazwie uiState. Jeśli interfejs wyświetla wiele niepowiązanych ze sobą danych, maszyna wirtualna może udostępniać wiele właściwości stanu interfejsu.
|
Poniższy fragment kodu pokazuje, jak udostępnić stan interfejsu z klasy 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
Stosuj sprawdzone metody dotyczące cyklu życia aktywności:
| Rekomendacja | Opis |
|---|---|
Używaj w kompozycjach efektów uwzględniających cykl życia zamiast zastępować wywołania zwrotne cyklu życia Activity.
Zdecydowanie zalecane |
Nie zastępuj metod cyklu życia
|
Poniższy fragment kodu pokazuje, jak wykonywać operacje w określonym stanie cyklu życia:
@Composable
fun LocationChangedEffect(
locationManager: LocationManager,
onLocationChanged: (Location) -> Unit
) {
val currentOnLocationChanged by rememberUpdatedState(onLocationChanged)
LifecycleStartEffect(locationManager) {
val listener = LocationListener { newLocation ->
currentOnLocationChanged(newLocation)
}
try {
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
1000L,
1f,
listener,
)
} catch (e: SecurityException) {
// TODO: Handle missing permissions
}
onStopOrDispose {
locationManager.removeUpdates(listener)
}
}
}
Obsługa zależności
Podczas zarządzania zależnościami między komponentami postępuj zgodnie ze sprawdzonymi metodami:
| Rekomendacja | Opis |
|---|---|
| Używaj wstrzykiwania zależności.
Zdecydowanie zalecane |
Stosuj sprawdzone metody wstrzykiwania zależności, a w miarę możliwości korzystaj głównie z wstrzykiwania przez konstruktor. |
| W razie potrzeby ogranicz zakres do komponentu.
Zdecydowanie zalecane |
Ogranicz zakres do kontenera zależności, gdy typ zawiera modyfikowalne dane, które muszą być udostępniane, lub gdy typ jest kosztowny w inicjalizacji i jest szeroko stosowany w aplikacji. |
| Użyj Hilt.
Wybrane dla Ciebie |
W prostych aplikacjach używaj Hilta lub ręcznego wstrzykiwania zależności. Używaj Hilt, jeśli projekt jest wystarczająco złożony, np. zawiera którykolwiek z tych elementów:
|
Testowanie
Oto kilka sprawdzonych metod dotyczących testowania:
| Rekomendacja | Opis |
|---|---|
| Wiedzieć, co testować
Zdecydowanie zalecane |
Jeśli projekt nie jest tak prosty jak aplikacja „hello world”, przetestuj go. Powinny one zawierać co najmniej te informacje:
|
| Preferuj obiekty zastępcze zamiast atrap.
Zdecydowanie zalecane |
Więcej informacji o używaniu obiektów zastępczych znajdziesz w artykule Używanie obiektów zastępczych w Androidzie. |
| Testowanie StateFlows.
Zdecydowanie zalecane |
Podczas testowania StateFlow wykonaj te czynności:
|
Więcej informacji znajdziesz w artykułach Co testować na Androidzie i Testowanie układu Compose.
Wydajność
Oto kilka sprawdzonych metod poprawy skuteczności:
| Rekomendacja | Opis |
|---|---|
| Użyj R8, aby zmniejszyć rozmiar aplikacji i ją zoptymalizować.
Zdecydowanie zalecane |
Włącz R8 w trybie pełnym w konfiguracji kompilacji, aby usunąć nieużywany kod, zaciemnić klasy i przepisać kod w celu zwiększenia wydajności w czasie działania. |
| wdrożyć profile podstawowe i profile uruchamiania;
Zdecydowanie zalecane |
Generuj profile podstawowe i profile uruchamiania, aby wstępnie skompilować krytyczne ścieżki kodu. Znacznie skraca to czas oczekiwania na uruchomienie (TTID i TTFD) oraz minimalizuje zacinanie się i błędy ANR. |
| Testowanie wydajności za pomocą Macrobenchmark.
Zdecydowanie zalecane |
Użyj biblioteki Macrobenchmark, aby przetestować wydajność aplikacji i zapobiec regresji. |
Modele
Podczas tworzenia modeli w aplikacjach stosuj te sprawdzone metody:
| Rekomendacja | Opis |
|---|---|
| W przypadku złożonych aplikacji utwórz model dla każdej warstwy.
Wybrane dla Ciebie |
W przypadku złożonych aplikacji twórz nowe modele w różnych warstwach lub komponentach, gdy ma to sens. Oto przykłady:
|
Konwencje nazewnictwa
Podczas nadawania nazwy bazie kodu warto skorzystać z tych sprawdzonych metod:
| Rekomendacja | Opis |
|---|---|
| Metody nadawania nazw.
Opcjonalnie |
Do nazywania metod używaj wyrażeń czasownikowych, np. makePayment(). |
| Nazwy właściwości.
Opcjonalnie |
Do nazywania właściwości używaj wyrażeń rzeczownikowych, np. inProgressTopicSelection. |
| Nazewnictwo strumieni danych.
Opcjonalnie |
Gdy klasa udostępnia strumień Flow lub inny strumień, obowiązuje konwencja nazewnictwa get{model}Stream, np. getAuthorStream(): Flow<Author>.
Jeśli funkcja zwraca listę modeli, użyj nazwy modelu w liczbie mnogiej: getAuthorsStream(): Flow<List<Author>>. |
| Nazewnictwo implementacji interfejsów.
Opcjonalnie |
Nadawaj implementacjom interfejsów rozpoznawalne nazwy. Jeśli nie można 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.
Dodaj prefiks Fake do fałszywych implementacji, np. FakeAuthorsRepository. |
Dodatkowe materiały
Więcej informacji o architekturze Androida znajdziesz w tych materiałach: