Auf dieser Seite finden Sie mehrere Architektur-Best Practices und -Empfehlungen. Wenn Sie sie übernehmen, können Sie die Qualität, Robustheit und Skalierbarkeit Ihrer App verbessern. Außerdem erleichtern sie die Wartung und das Testen Ihrer App.
Die folgenden Best Practices sind nach Thema gruppiert. Jede Empfehlung hat eine Priorität, die angibt, wie wichtig sie ist. Die Prioritäten sind:
- Dringend empfohlen:Implementieren Sie diese Vorgehensweise, sofern sie nicht grundlegend mit Ihrem Ansatz kollidiert.
- Empfohlen:Diese Vorgehensweise wird Ihre App wahrscheinlich verbessern.
- Optional:Unter bestimmten Umständen kann diese Vorgehensweise die Leistung Ihrer App verbessern.
Geschichtete Architektur
Unsere empfohlene geschichtete Architektur begünstigt die Trennung von Belangen. Sie leitet die Benutzeroberfläche aus Datenmodellen ab, entspricht dem Prinzip der Single Source of Truth und folgt den Prinzipien des unidirektionalen Datenflusses. Hier sind 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 |
In der UI-Ebene werden die Anwendungsdaten auf dem Bildschirm angezeigt. Sie ist der primäre Punkt für die Nutzerinteraktion. Jetpack Compose ist das empfohlene moderne Toolkit für die Entwicklung der Benutzeroberfläche Ihrer App.
|
| Anwendungsdaten aus der Datenschicht über ein Repository verfügbar machen.
Dringend empfohlen |
Achten Sie darauf, dass Komponenten in der UI-Schicht wie Composables oder ViewModels nicht direkt mit einer Datenquelle interagieren. Beispiele für Datenquellen:
|
| Verwenden Sie Coroutines und Flows.
Dringend empfohlen |
Verwenden Sie Coroutinen und Flows für die Kommunikation zwischen den Ebenen.
Weitere Informationen zu Best Practices für Coroutinen finden Sie unter Best Practices für Coroutinen in Android. |
| Verwenden Sie eine Domain-Ebene.
Empfohlen für große Apps |
Verwenden Sie eine Domänenebene mit Anwendungsfällen, wenn Sie Geschäftslogik, die mit der Datenschicht interagiert, in mehreren ViewModels wiederverwenden 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 darzustellen und als primärer Punkt für die Nutzerinteraktion zu fungieren. Hier sind einige Best Practices für die UI-Ebene:
| Empfehlung | Beschreibung |
|---|---|
| Folgen Sie dem unidirektionalen Datenfluss (UDF).
Dringend empfohlen |
Halten Sie sich an die Prinzipien des unidirektionalen Datenflusses (Unidirectional Data Flow, UDF), bei denen ViewModels den UI-Status über das Observer-Muster bereitstellen und Aktionen über Methodenaufrufe von der UI 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 Anwendungsdaten abzurufen, um den UI-Status für die Benutzeroberfläche verfügbar zu machen.
Weitere Informationen zu ViewModel-Best Practices finden Sie unter Architekturempfehlungen. Weitere Informationen zu den Vorteilen von ViewModels finden Sie unter Das ViewModel als Status-Holder für die Geschäftslogik. |
| Lebenszyklusbewusste Erfassung des UI-Zustands verwenden
Dringend empfohlen |
Erfassen Sie den UI-Status über die UI mit dem entsprechenden lebenszyklusbezogenen Coroutine-Builder collectAsStateWithLifecycle.
|
| Senden Sie keine Ereignisse vom ViewModel an die Benutzeroberfläche.
Dringend empfohlen |
Verarbeiten Sie das Ereignis sofort im ViewModel und lösen Sie mit dem Ergebnis der Verarbeitung des Ereignisses eine Statusaktualisierung aus. Weitere Informationen zu UI-Ereignissen finden Sie unter ViewModel-Ereignisse verarbeiten. |
| Verwenden Sie eine App mit nur einer Aktivität.
Dringend empfohlen |
Verwenden Sie Navigation 3, um zwischen Bildschirmen zu wechseln und einen Deeplink zu Ihrer App zu erstellen, wenn sie mehrere Bildschirme hat. |
| Verwenden Sie Jetpack Compose.
Dringend empfohlen |
Mit Jetpack Compose können Sie neue Apps für Smartphones, Tablets, Faltgeräte und Wear OS entwickeln. |
Im folgenden Snippet wird beschrieben, wie der UI-Zustand auf lebenszyklusbewusste Weise erfasst wird:
@Composable
fun MyScreen(
viewModel: MyViewModel = viewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
}
ViewModel
ViewModels sind dafür verantwortlich, den UI-Status bereitzustellen und auf die Datenschicht zuzugreifen. Hier sind einige Best Practices für ViewModels:
| Empfehlung | Beschreibung |
|---|---|
| ViewModels unabhängig vom Android-Lebenszyklus halten
Dringend empfohlen |
ViewModel-Klassen sollten keine Referenz auf einen typischen Lifecycle-Typ enthalten. Übergeben Sie Activity, Context oder Resources nicht als Abhängigkeit.
Wenn etwas ein Context im ViewModel erfordert, prüfen Sie sorgfältig, ob es sich um die richtige Ebene handelt. |
| Verwenden Sie Coroutines und Flows.
Dringend empfohlen |
Das ViewModel interagiert mit den Daten- oder Domänenebenen über Folgendes:
|
| ViewModels auf Bildschirmebene verwenden
Dringend empfohlen |
Verwenden Sie ViewModels nicht in wiederverwendbaren UI-Elementen. ViewModels sollten in folgenden Fällen verwendet werden:
|
| Verwenden Sie einfache Status-Holder-Klassen in wiederverwendbaren UI-Komponenten.
Dringend empfohlen |
Verwenden Sie einfache Status-Holder-Klassen, um Komplexität in wiederverwendbaren UI-Komponenten zu verarbeiten. Dadurch kann der Status nach oben verschoben und extern gesteuert werden. |
Verwenden Sie nicht AndroidViewModel.
Empfohlen |
Verwenden Sie die Klasse ViewModel, nicht AndroidViewModel. Verwenden Sie die Klasse Application nicht im ViewModel. Verschieben Sie die Abhängigkeit stattdessen in die UI- oder Datenebene. |
| UI-Zustand bereitstellen
Empfohlen |
Stellen Sie Daten für die Benutzeroberfläche über eine einzelne Property namens uiState in Ihren ViewModels bereit. Wenn in der Benutzeroberfläche mehrere unabhängige Daten angezeigt werden, kann die VM mehrere Eigenschaften für den UI-Status bereitstellen.
|
Das folgende Snippet zeigt, wie Sie den UI-Status aus einem ViewModel bereitstellen:
@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
Beachten Sie die Best Practices für die Arbeit mit dem Aktivitätslebenszyklus:
| Empfehlung | Beschreibung |
|---|---|
Verwenden Sie in Composables lebenszyklusbezogene Effekte, anstatt die Activity-Lebenszyklus-Callbacks zu überschreiben.
Dringend empfohlen |
Überschreiben Sie keine
|
Das folgende Snippet zeigt, wie Sie Vorgänge in einem bestimmten Lebenszyklusstatus ausführen:
@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)
}
}
}
Abhängigkeiten verarbeiten
Beachten Sie beim Verwalten von Abhängigkeiten zwischen Komponenten die folgenden Best Practices:
| Empfehlung | Beschreibung |
|---|---|
| Abhängigkeitsinjektion verwenden
Dringend empfohlen |
Verwenden Sie die Best Practices für die Abhängigkeitsinjektion, insbesondere die Konstruktorinjektion, wann immer möglich. |
| Bei Bedarf auf eine Komponente beschränken.
Dringend empfohlen |
Beschränken Sie den Umfang auf einen Abhängigkeitscontainer, wenn der Typ veränderliche Daten enthält, die freigegeben werden müssen, oder wenn der Typ teuer zu initialisieren ist und in der App häufig verwendet wird. |
| Verwenden Sie Hilt.
Empfohlen |
Verwenden Sie Hilt oder manuelle Abhängigkeitsinjektion in einfachen Apps. Verwenden Sie Hilt, wenn Ihr Projekt komplex genug ist, z. B. wenn es Folgendes enthält:
|
Testen
Im Folgenden finden Sie einige Best Practices für das Testen:
| Empfehlung | Beschreibung |
|---|---|
| Testgegenstand
Dringend empfohlen |
Sofern es sich nicht um ein einfaches „Hello World“-Projekt handelt, sollten Sie es testen. Geben Sie mindestens Folgendes an:
|
| Fakes werden gegenüber Mocks bevorzugt.
Dringend empfohlen |
Weitere Informationen zur Verwendung von Fakes finden Sie unter Test Doubles in Android verwenden. |
| StateFlows testen
Dringend empfohlen |
Gehen Sie beim Testen von StateFlow so vor:
|
Weitere Informationen finden Sie unter Was sollte in Android getestet werden? und Compose-Layout testen.
Modelle
Beachten Sie die folgenden Best Practices, wenn Sie Modelle in Ihren Apps entwickeln:
| Empfehlung | Beschreibung |
|---|---|
| Erstellen Sie ein Modell pro Ebene in komplexen Apps.
Empfohlen |
In komplexen Apps sollten Sie neue Modelle in verschiedenen Ebenen oder Komponenten erstellen, wenn es sinnvoll ist. Hier einige Beispiele:
|
Namenskonventionen
Beachten Sie beim Benennen Ihres Quellcodes die folgenden Best Practices:
| Empfehlung | Beschreibung |
|---|---|
| Benennungsmethoden
Optional |
Verwenden Sie Verbgruppen, um Methoden zu benennen, z. B. makePayment(). |
| Benennung von Eigenschaften
Optional |
Verwenden Sie Wortgruppen, um Eigenschaften zu benennen, z. B. inProgressTopicSelection. |
| Datenstreams benennen
Optional |
Wenn eine Klasse einen Flow-Stream oder einen anderen Stream bereitstellt, lautet die Namenskonvention get{model}Stream. Beispiel: getAuthorStream(): Flow<Author>.
Wenn die Funktion eine Liste von Modellen zurückgibt, verwenden Sie den Plural des Modellnamens: getAuthorsStream(): Flow<List<Author>>. |
| Benennung von Schnittstellenimplementierungen
Optional |
Verwenden Sie aussagekräftige Namen für die Implementierungen von Schnittstellen. Verwenden Sie Default als Präfix, wenn kein besserer Name gefunden werden kann. Für eine NewsRepository-Schnittstelle haben Sie möglicherweise eine OfflineFirstNewsRepository oder InMemoryNewsRepository. Wenn Sie keinen passenden Namen finden, verwenden Sie DefaultNewsRepository.
Stellen Sie fiktiven Implementierungen das Präfix Fake voran, z. B. FakeAuthorsRepository. |
Zusätzliche Ressourcen
Weitere Informationen zur Android-Architektur finden Sie in den folgenden zusätzlichen Ressourcen: