Auf dieser Seite finden Sie verschiedene Best Practices und Empfehlungen zur Architektur. Setzen Sie sie ein, um die Qualität, Robustheit und Skalierbarkeit Ihrer App zu verbessern. Sie erleichtern außerdem die Wartung und das Testen Ihrer App.
Die folgenden Best Practices sind nach Themen gruppiert. Jede Priorität hat eine Priorität, die angibt, wie stark das Team sie empfiehlt. Die Liste der Prioritäten sieht so aus:
- Dringend empfohlen: Sie sollten diese Vorgehensweise implementieren, es sei denn, sie steht im Widerspruch zu Ihrem Ansatz grundlegend zur Verfügung.
- Empfohlen:Damit können Sie Ihre App wahrscheinlich verbessern.
- Optional:Durch diese Vorgehensweise kann Ihre App unter bestimmten Umständen verbessert werden.
Mehrschichtige Architektur
Unsere empfohlene mehrschichtige Architektur bevorzugt eine Trennung der Problembereiche. Sie stützt die UI aus Datenmodellen, entspricht dem Single-Source-of-Truth-Prinzip und folgt den Prinzipien des unidirektionalen Datenflusses. Hier sind einige Best Practices für mehrschichtige Architekturen:
Empfehlung | Beschreibung |
---|---|
Verwenden Sie eine klar definierte Datenschicht.
Dringend empfohlen |
Die Datenschicht 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 Punkt für die Nutzerinteraktion.
|
Die Datenschicht sollte Anwendungsdaten mithilfe eines Repositorys bereitstellen.
Dringend empfohlen |
Komponenten auf der UI-Ebene wie zusammensetzbare Funktionen, Aktivitäten oder ViewModels sollten nicht direkt mit einer Datenquelle interagieren. Beispiele für Datenquellen:
|
Verwenden Sie Koroutinen und Abläufe.
Dringend empfohlen |
Verwenden Sie Koroutinen und Abläufe für die Kommunikation zwischen Layern. |
Verwenden Sie eine Domainebene.
In großen Apps empfohlen |
Verwenden Sie eine Domainebene und Anwendungsfälle, wenn Sie Geschäftslogik wiederverwenden müssen, die mit der Datenschicht über mehrere ViewModels hinweg interagiert, oder wenn Sie die Komplexität der Geschäftslogik eines bestimmten ViewModel-Objekts vereinfachen möchten. |
UI-Ebene
Die UI-Ebene dient dazu, die Anwendungsdaten auf dem Bildschirm anzuzeigen und als primärer Punkt für die Nutzerinteraktion zu dienen. Hier sind einige Best Practices für die UI-Ebene:
Empfehlung | Beschreibung |
---|---|
Folgen Sie dem Unidirektionalen Datenfluss (UDF).
Dringend empfohlen |
Folgen Sie den Prinzipien des Unidirektionalen Datenflusses (UDF), bei dem ViewModels den UI-Status mithilfe des Beobachtermusters ermitteln und Aktionen von der Benutzeroberfläche über Methodenaufrufe empfangen. |
Verwenden Sie AAC ViewModels, wenn deren Vorteile für Ihre App gelten.
Dringend empfohlen |
Verwenden Sie AAC ViewModels, um die Geschäftslogik zu verarbeiten, und rufen Sie Anwendungsdaten ab, um den UI-Status für die Benutzeroberfläche (Compose oder Android-Ansichten) anzuzeigen. |
Lebenszyklusabhängige UI-Statuserfassung verwenden.
Dringend empfohlen |
Erfassen Sie den Status der Benutzeroberfläche mit dem entsprechenden Builder für lebenszyklusfähige Koroutinen: repeatOnLifecycle im View-System und collectAsStateWithLifecycle in Jetpack Compose.
Weitere Informationen zu Weitere Informationen zu |
Senden Sie keine Ereignisse von ViewModel an die UI.
Dringend empfohlen |
Verarbeiten Sie das Ereignis sofort in ViewModel und führen Sie mit dem Ergebnis der Verarbeitung des Ereignisses eine Statusaktualisierung aus. Weitere Informationen zu UI-Ereignissen |
Verwenden Sie eine App mit nur einer Aktivität.
Empfohlen |
Wenn Ihre App mehr als einen Bildschirm hat, verwenden Sie Navigationsfragmente oder Navigationselemente verfassen, um zwischen Bildschirmen zu wechseln und einen Deeplink zu Ihrer App hinzuzufügen. |
Verwenden Sie Jetpack Compose.
Empfohlen |
Mit Jetpack Compose können Sie neue Apps für Smartphones, Tablets, faltbare Smartphones und Wear OS-Apps entwickeln. |
Im folgenden Snippet wird beschrieben, wie Sie den Status der UI unter Berücksichtigung des Lebenszyklus erfassen:
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 den Zugriff auf die Datenschicht verantwortlich. Hier sind einige Best Practices für ViewModels:
Empfehlung | Beschreibung |
---|---|
ViewModels sollten unabhängig vom Android-Lebenszyklus sein.
Dringend empfohlen |
ViewModels sollte keinen Verweis auf einen Lebenszyklus-bezogenen Typ enthalten. Übergeben Sie Activity, Fragment, Context oder Resources nicht als Abhängigkeit.
Wenn ein Context in der ViewModel-Datei erforderlich ist, sollten Sie unbedingt prüfen, ob sich diese Ebene auf der richtigen Ebene befindet. |
Verwenden Sie Koroutinen und Abläufe.
Dringend empfohlen |
Das ViewModel interagiert mit den Daten- oder Domainebenen folgendermaßen:
|
ViewModels auf Bildschirmebene verwenden.
Dringend empfohlen |
Verwenden Sie ViewModels nicht in wiederverwendbaren UI-Elementen. Sie sollten ViewModels in folgenden Ländern verwenden:
|
Verwenden Sie in wiederverwendbaren UI-Komponenten einfache Halterungsklassen.
Dringend empfohlen |
Verwenden Sie einfache Zustandshalterklassen, um die Komplexität von wiederverwendbaren UI-Komponenten zu bewältigen. Dadurch kann der Zustand extern aufgezogen und gesteuert werden. |
Verwenden Sie AndroidViewModel nicht.
Empfohlen |
Verwenden Sie den Kurs ViewModel , nicht AndroidViewModel . Die Klasse Application sollte in ViewModel nicht verwendet werden. Verschieben Sie die Abhängigkeit stattdessen in die Benutzeroberfläche oder die Datenschicht. |
Gibt einen UI-Status frei.
Empfohlen |
ViewModels sollte Daten über eine einzelne Property namens uiState für die UI verfügbar machen. Wenn in der UI mehrere nicht zusammenhängende Daten angezeigt werden, kann die VM mehrere UI-Statusattribute zur Verfügung stellen.
|
Das folgende Snippet zeigt, wie der UI-Status einer ViewModel-Ressource angezeigt 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 keine Lebenszyklusmethoden in „Aktivitäten“ oder „Fragmente“.
Dringend empfohlen |
Überschreiben Sie keine Lebenszyklusmethoden wie onResume in „Aktivitäten“ oder „Fragmente“. Verwenden Sie stattdessen LifecycleObserver . Wenn die Anwendung Aufgaben ausführen muss, wenn der Lebenszyklus einen bestimmten Lifecycle.State erreicht, verwenden Sie die repeatOnLifecycle API. |
Das folgende Snippet beschreibt, 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 verarbeiten
Es gibt mehrere Best Practices, die Sie beim Verwalten von Abhängigkeiten zwischen Komponenten beachten sollten:
Empfehlung | Beschreibung |
---|---|
Verwenden Sie die Abhängigkeitsinjektion.
Dringend empfohlen |
Wenden Sie die Best Practices für die Abhängigkeitsinjektion an, vor allem die Konstruktor-Injektion. |
Wechseln Sie bei Bedarf zu einer Komponente.
Dringend empfohlen |
Wechseln Sie zu einem Abhängigkeitscontainer, wenn der Typ änderbare Daten enthält, die freigegeben werden müssen, oder wenn die Initialisierung des Typs teuer ist und in der App weit verbreitet ist. |
Verwenden Sie den Hilt.
Empfohlen |
Verwenden Sie in einfachen Apps Hilt oder manuelle Abhängigkeitsinjektion. Verwenden Sie Hilt, wenn Ihr Projekt komplex genug ist. Beispiel:
|
Testen
Im Folgenden finden Sie einige Best Practices für Tests:
Empfehlung | Beschreibung |
---|---|
Wissen, was getestet werden muss:
Dringend empfohlen |
Wenn das Projekt nicht grob so einfach ist wie eine Hello World-Anwendung, sollten Sie es zumindest mit folgenden Elementen testen:
|
Fälschungen gegenüber Simulationen bevorzugen.
Dringend empfohlen |
Weitere Informationen finden Sie in der Dokumentation zur Verwendung von Test-Doubles in der Android-Dokumentation. |
StateFlows testen.
Dringend empfohlen |
Beim Testen von StateFlow :
|
Weitere Informationen finden Sie im Leitfaden zu Tests im Android-DAC.
Fotomodelle
Halten Sie sich bei der Entwicklung von Modellen in Ihren Apps an die folgenden Best Practices:
Empfehlung | Beschreibung |
---|---|
Erstellen Sie in komplexen Apps ein Modell pro Ebene.
Empfohlen |
Erstellen Sie in komplexen Apps neue Modelle in verschiedenen Ebenen oder Komponenten, wenn es sinnvoll ist. Betrachten Sie die folgenden Beispiele:
|
Namenskonventionen
Beachten Sie beim Benennen Ihrer Codebasis die folgenden Best Practices:
Empfehlung | Beschreibung |
---|---|
Benennungsmethoden
Optional |
Methoden sollten eine Verb-Phrase sein. z. B. makePayment() . |
Eigenschaften benennen
Optional |
Eigenschaften sollten eine Nominalphrase sein. z. B. 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 in der Pluralform vorliegen: getAuthorsStream(): Flow<List<Author>> |
Schnittstellenimplementierungen benennen
Optional |
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, wie in FakeAuthorsRepository . |