Auf dieser Seite finden Sie mehrere Best Practices und Empfehlungen für die Architektur. Sie können sie verwenden, um die Qualität, Robustheit und Skalierbarkeit Ihrer App zu verbessern. Außerdem erleichtern sie die Wartung und das Testen Ihrer App.
Die folgenden Best Practices sind nach Thema gruppiert. Jede hat eine Priorität, die widerspiegelt, wie sehr das Team sie empfiehlt. Die Prioritätenliste sieht so aus:
- Dringend empfohlen:Sie sollten diese Praxis implementieren, es sei denn, sie steht grundsätzlich im Widerspruch zu Ihrem Ansatz.
- Empfohlen:Dadurch lässt sich Ihre App wahrscheinlich verbessern.
- Optional:Diese Vorgehensweise kann Ihre App unter bestimmten Umständen verbessern.
Mehrschichtige Architektur
Unsere empfohlene mehrschichtige Architektur fördert die Aufgabentrennung. Die Benutzeroberfläche basiert auf Datenmodellen, entspricht dem Prinzip der Single Source of Truth und folgt den Prinzipien des einseitigen Datenflusses. Im Folgenden finden Sie einige Best Practices für die mehrschichtige Architektur:
Empfehlung | Beschreibung |
---|---|
Verwenden Sie eine klar definierte Datenschicht.
Dringend empfohlen |
Die Datenebene stellt Anwendungsdaten für den Rest der App bereit und enthält den Großteil der Geschäftslogik Ihrer App.
|
Verwenden Sie eine klar definierte UI-Ebene.
Dringend empfohlen |
Die UI-Ebene zeigt die Anwendungsdaten auf dem Bildschirm an und dient als primärer Interaktionspunkt für Nutzer.
|
Die Datenschicht sollte Anwendungsdaten über ein Repository bereitstellen.
Dringend empfohlen |
Komponenten in der UI-Ebene wie Composeables, Aktivitäten oder ViewModels sollten nicht direkt mit einer Datenquelle interagieren. Beispiele für Datenquellen:
|
Verwenden Sie Coroutinen und Abläufe.
Dringend empfohlen |
Verwenden Sie Koroutinen und Abläufe, um zwischen Ebenen zu kommunizieren. |
Verwenden Sie eine Domainebene.
In großen Apps empfohlen |
Verwenden Sie eine Domainebene, wenn Sie Geschäftslogik, die mit der Datenebene in mehreren ViewModels interagiert, wiederverwenden möchten oder die Komplexität der Geschäftslogik eines bestimmten ViewModels vereinfachen möchten. |
UI-Ebene
Die UI-Ebene dient dazu, die Anwendungsdaten auf dem Bildschirm anzuzeigen und als primärer Interaktionspunkt für Nutzer zu dienen. Hier sind einige Best Practices für die UI-Ebene:
Empfehlung | Beschreibung |
---|---|
Folgen Sie dem einseitigen Datenfluss (Unidirectional Data Flow, UDF).
Dringend empfohlen |
Beachten Sie die Prinzipien des einseitigen Datenflusses (Unidirectional Data Flow, UDF), bei denen ViewModels den UI-Status mithilfe des Beobachtermusters freigeben und Aktionen von der UI über Methodenaufrufe empfangen. |
Verwenden Sie AAC-ViewModels, wenn die Vorteile für Ihre App gelten.
Dringend empfohlen |
Verwenden Sie AAC-ViewModels, um Geschäftslogik zu verarbeiten, und rufen Sie Anwendungsdaten ab, um den UI-Status für die UI (Compose- oder Android-Views) freizugeben. |
Verwenden Sie die lebenszyklusorientierte Erfassung des UI-Status.
Dringend empfohlen |
Erfassen Sie den UI-Status mit dem entsprechenden lebenszyklusbewussten Coroutinen-Builder: repeatOnLifecycle im View-System und collectAsStateWithLifecycle in Jetpack Compose.
Weitere Informationen zu Weitere Informationen zu |
Senden Sie keine Ereignisse von ViewModel an die Benutzeroberfläche.
Dringend empfohlen |
Das Ereignis wird sofort im ViewModel verarbeitet und es wird ein Statusupdate mit dem Ergebnis der Ereignisbearbeitung ausgelöst. Weitere Informationen zu UI-Ereignissen |
Verwenden Sie eine App mit einer einzelnen Aktivität.
Empfohlen |
Verwenden Sie Navigationsfragmente oder Navigation Compose, um zwischen Bildschirmen zu wechseln und einen Deeplink zu Ihrer App herzustellen, wenn Ihre App mehr als einen Bildschirm hat. |
Verwenden Sie Jetpack Compose.
Empfohlen |
Mit Jetpack Compose können Sie neue Apps für Smartphones, Tablets, faltbare Geräte und Wear OS entwickeln. |
Im folgenden Snippet wird beschrieben, wie der UI-Status laufzeitabhängig erfasst wird:
Aufrufe
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
}
}
}
}
}
Schreiben
@Composable
fun MyScreen(
viewModel: MyViewModel = viewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
}
ViewModel
ViewModels sind für die Bereitstellung des UI-Status und des Zugriffs auf die Datenschicht zuständig. Im Folgenden finden Sie einige Best Practices für ViewModels:
Empfehlung | Beschreibung |
---|---|
ViewModels sollten unabhängig vom Android-Lebenszyklus sein.
Dringend empfohlen |
ViewModels dürfen keine Referenz auf einen lebenszyklusbezogenen Typ enthalten. Übergeben Sie Activity, Fragment, Context oder Resources nicht als Abhängigkeit.
Wenn für etwas im ViewModel eine Context erforderlich ist, sollten Sie genau prüfen, ob sich das in der richtigen Schicht befindet. |
Verwenden Sie Coroutinen und Abläufe.
Dringend empfohlen |
Das ViewModel interagiert mit den Daten- oder Domainebenen über:
|
Verwenden Sie ViewModels auf Bildschirmebene.
Dringend empfohlen |
Verwenden Sie ViewModels nicht in wiederverwendbaren Elementen der Benutzeroberfläche. ViewModels sollten in folgenden Fällen verwendet werden:
|
Verwenden Sie einfache Statushalterklassen in wiederverwendbaren UI-Komponenten.
Dringend empfohlen |
Verwenden Sie einfache State-Holder-Klassen, um die Komplexität wiederverwendbarer UI-Komponenten zu bewältigen. Auf diese Weise kann der Zustand extern hochgezogen und gesteuert werden. |
Verwenden Sie nicht AndroidViewModel .
Empfohlen |
Verwenden Sie die Klasse ViewModel , nicht AndroidViewModel . Die Klasse Application sollte nicht im ViewModel verwendet werden. Verschieben Sie die Abhängigkeit stattdessen in die Benutzeroberfläche oder die Datenebene. |
Zeigt einen UI-Status an.
Empfohlen |
ViewModels sollte Daten über eine einzige Property namens uiState für die Benutzeroberfläche bereitstellen. Wenn die Benutzeroberfläche mehrere, nicht zusammenhängende Daten enthält, kann die VM mehrere UI-Statuseigenschaften freigeben.
|
Das folgende Snippet veranschaulicht, wie der UI-Status aus einer ViewModel-Ressource verfügbar gemacht wird:
@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
)
// ...
}
Lebenszyklus
Im Folgenden finden Sie einige Best Practices für die Arbeit mit dem Android-Lebenszyklus:
Empfehlung | Beschreibung |
---|---|
Überschreiben Sie die Lebenszyklusmethoden in Aktivitäten oder Fragmenten nicht.
Dringend empfohlen |
Überschreiben Sie Lebenszyklusmethoden wie onResume in Aktivitäten oder Fragmenten nicht. Verwenden Sie stattdessen LifecycleObserver . Wenn die Anwendung Arbeiten ausführen muss, wenn der Lebenszyklus einen bestimmten Lifecycle.State erreicht, verwenden Sie die repeatOnLifecycle API. |
Im folgenden Snippet wird beschrieben, wie Vorgänge bei einem bestimmten Lebenszyklusstatus ausgeführt werden:
Aufrufe
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) {
// ...
}
}
}
}
Schreiben
@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)
}
}
}
Abhängigkeiten behandeln
Es gibt mehrere Best Practices, die Sie bei der Verwaltung von Abhängigkeiten zwischen Komponenten beachten sollten:
Empfehlung | Beschreibung |
---|---|
Verwenden Sie die Abhängigkeitsinjektion.
Dringend empfohlen |
Verwenden Sie die Best Practices für die Dependency Injection, vor allem die Konstruktor-Injection, wenn möglich. |
Wechseln Sie bei Bedarf zu einer Komponente.
Dringend empfohlen |
Beschränken Sie den Gültigkeitsbereich auf einen Abhängigkeitscontainer, wenn der Typ veränderliche Daten enthält, die freigegeben werden müssen, oder wenn die Initialisierung des Typs aufwendig ist und er in der App häufig verwendet wird. |
Verwenden Sie Hilt.
Empfohlen |
Verwenden Sie Hilt oder die manuelle Abhängigkeitsinjektion in einfachen Apps. Verwenden Sie Hilt, wenn Ihr Projekt komplex genug ist. Zum Beispiel:
|
Testen
Hier einige Best Practices für Tests:
Empfehlung | Beschreibung |
---|---|
Wissen, was getestet werden soll
Dringend empfohlen |
Sofern das Projekt nicht ungefähr so einfach wie eine „Hello World“-Anwendung ist, sollten Sie es mindestens mit folgenden Elementen testen:
|
Verwenden Sie lieber Fakes als Mockups.
Dringend empfohlen |
Weitere Informationen finden Sie in der Android-Dokumentation zum Verwenden von Test-Doubles. |
Testen Sie StateFlows.
Dringend empfohlen |
Beim Testen von StateFlow :
|
Weitere Informationen finden Sie im Leitfaden Was im Android-DAC-Test geprüft werden sollte.
Modelle
Beachten Sie beim Entwickeln von Modellen in Ihren Apps die folgenden Best Practices:
Empfehlung | Beschreibung |
---|---|
Ein Modell pro Ebene in komplexen Apps erstellen
Empfohlen |
Erstellen Sie in komplexen Apps neue Modelle in verschiedenen Ebenen oder Komponenten, wenn es sinnvoll ist. Betrachten Sie hierzu folgende Beispiele:
|
Namenskonventionen
Beachten Sie beim Benennen Ihrer Codebasis die folgenden Best Practices:
Empfehlung | Beschreibung |
---|---|
Benennungsmethoden
Optional |
Methoden sollten Verben sein. Beispiel: makePayment() . |
Namenskonventionen für Properties
Optional |
Unterkünfte sollten als Substantivphrase angegeben werden. Beispiel: inProgressTopicSelection . |
Datenstreams benennen
Optional |
Wenn eine Klasse einen Flow-Stream, LiveData oder einen anderen Stream verfügbar macht, lautet die Namenskonvention get{model}Stream() . Beispiel: getAuthorStream(): Flow<Author>
Wenn die Funktion eine Liste von Modellen zurückgibt, sollte der Modellname im Plural stehen: getAuthorsStream(): Flow<List<Author>> |
Namenskonventionen für die Implementierung von Schnittstellen
Optional |
Die Namen für die Implementierungen von Schnittstellen sollten aussagekräftig sein. Verwenden Sie Default als Präfix, wenn kein besserer Name gefunden werden kann. Für eine NewsRepository -Schnittstelle könnten Sie beispielsweise OfflineFirstNewsRepository oder InMemoryNewsRepository verwenden. Wenn Sie keinen guten Namen finden, verwenden Sie DefaultNewsRepository .
Gefälschte Implementierungen müssen das Präfix Fake haben, z. B. FakeAuthorsRepository . |