Empfehlungen für die Android-Architektur

Auf dieser Seite werden mehrere Best Practices und Empfehlungen für die Architektur vorgestellt. Nimm sie an, um die Qualität, Stabilität und Skalierbarkeit deiner App zu verbessern. Außerdem die Wartung und das Testen Ihrer App erleichtern.

Die folgenden Best Practices sind nach Themen gruppiert. Jedes hat eine Priorität, die wie stark das Team sie empfiehlt. Die Prioritätenliste sieht so aus:

  • Dringende Empfehlung:Sie sollten diese Vorgehensweise implementieren, es sei denn, es besteht ein Konflikt Ihre Herangehensweise.
  • Empfohlen:Durch diese Vorgehensweise wird Ihre App wahrscheinlich verbessert.
  • Optional:Durch diese Vorgehensweise kann Ihre App unter bestimmten Umständen verbessert werden.

Mehrschichtige Architektur

Unsere empfohlene mehrstufige Architektur bevorzugt die Trennung von Belangen. Es unterstützt die Benutzeroberfläche aus Datenmodellen, entspricht dem Prinzip der Single Source of Truth, und folgt den Prinzipien des unidirektionalen Datenflusses. Hier sind einige der besten mehrschichtige Architektur:

Empfehlung Beschreibung
Verwenden Sie eine klar definierte Datenschicht.
Dringend empfohlen
Über die Datenschicht sind die Anwendungsdaten für den Rest der App verfügbar und sie enthält den Großteil der Geschäftslogik der App. <ph type="x-smartling-placeholder">
    </ph>
  • Sie sollten Repositories erstellen, auch wenn sie nur eine einzige Datenquelle enthalten.
  • In kleinen Apps können Sie Datenschichttypen in einem data-Paket oder -Modul platzieren.
Verwenden Sie eine klar definierte UI-Ebene.
Dringend empfohlen
Die UI-Ebene stellt die Anwendungsdaten auf dem Bildschirm dar und dient als primärer Punkt der Nutzerinteraktion.
  • In kleinen Apps können Sie Datenschichttypen in einem ui-Paket oder -Modul platzieren.
Weitere Best Practices für UI-Ebenen
Die Datenschicht sollte Anwendungsdaten über ein Repository verfügbar machen.
Dringend empfohlen

Komponenten auf der UI-Ebene wie zusammensetzbare Funktionen, Aktivitäten oder ViewModels dürfen nicht direkt mit einer Datenquelle interagieren. Beispiele für Datenquellen:

  • Datenbanken, Datenspeicher, SharedPreferences, Firebase APIs.
  • GPS-Standortanbieter
  • Bluetooth-Datenanbieter
  • Anbieter des Netzwerkverbindungsstatus.
Koroutinen und Abläufe verwenden.
Dringend empfohlen
Verwenden Sie Koroutinen und Abläufe, um zwischen Ebenen zu kommunizieren.

Weitere Best Practices für gemeinsame Abläufe

Verwenden Sie eine Domainebene.
Empfohlen in großen Apps
Verwenden Sie eine Domainebene, wenn Sie die Geschäftslogik in mehreren ViewModels wiederverwenden oder die Geschäftslogik eines bestimmten ViewModel vereinfachen möchten.

UI-Ebene

Die UI-Ebene hat die Aufgabe, die Anwendungsdaten auf dem Bildschirm darzustellen. und dienen als primärer Punkt der Nutzerinteraktion. Hier sind einige Best Practices, für die UI-Ebene:

Empfehlung Beschreibung
Folgen Sie der Anleitung unter Unidirektionaler Datenfluss (UDF).
Dringend empfohlen
Befolgen Sie die Prinzipien des unidirektionalen Datenflusses (UDF), wobei ViewModels den Status der Benutzeroberfläche mithilfe des Beobachtermusters verfügbar machen und durch Methodenaufrufe Aktionen von der Benutzeroberfläche empfangen.
Verwenden Sie AAC ViewModels, wenn sich deren Vorteile auf Ihre App auswirken.
Dringend empfohlen
Verwenden Sie AAC ViewModels zur Verarbeitung der Geschäftslogik und rufen Sie Anwendungsdaten ab, um den UI-Status der Benutzeroberfläche (Compose oder Android Views) anzuzeigen.

Weitere Best Practices für ViewModel

Weitere Informationen zu den Vorteilen von ViewModels

Verwenden Sie die lebenszyklusorientierte Erfassung des UI-Status.
Dringend empfohlen
Sie können den UI-Status mit dem entsprechenden Builder für Lifecycle-Events über die UI erfassen: repeatOnLifecycle im View-System und collectAsStateWithLifecycle in Jetpack Compose.

Weitere Informationen zu repeatOnLifecycle.

Weitere Informationen zu collectAsStateWithLifecycle.

Senden Sie keine Ereignisse von ViewModel an die Benutzeroberfläche.
Dringend empfohlen
Verarbeitet das Ereignis sofort in ViewModel und führt zu einer Statusaktualisierung mit dem Ergebnis der Verarbeitung des Ereignisses. Weitere Informationen zu UI-Ereignissen
Verwenden Sie eine Anwendung mit nur einer Aktivität.
Empfohlen
Verwenden Sie Navigationsfragmente oder Navigationseditor, um zwischen Bildschirmen zu wechseln und einen Deeplink zu Ihrer App zu erstellen, wenn Ihre App mehr als einen Bildschirm hat.
Verwenden Sie Jetpack Compose.
Empfohlen
Mit Jetpack Compose lassen sich neue Apps für Smartphones, Tablets, faltbare Geräte und Wear OS entwickeln.

Das folgende Snippet veranschaulicht, wie Sie den UI-Status in einem Art und Weise:

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 der Datenschicht. Hier sind einige Best Practices für ViewModels:

Empfehlung Beschreibung
ViewModels sollte unabhängig vom Android-Lebenszyklus sein.
Dringend empfohlen
ViewModels sollte keinen Verweis auf einen Lebenszyklustyp enthalten. Übergeben Sie Activity, Fragment, Context oder Resources nicht als Abhängigkeit. Wenn für etwas in ViewModel ein Context erforderlich ist, sollten Sie unbedingt prüfen, ob sich das in der richtigen Ebene befindet.
Koroutinen und Abläufe verwenden.
Dringend empfohlen

ViewModel interagiert mit den Daten- oder Domainebenen mithilfe von:

  • Kotlin-Abläufe für den Empfang von Anwendungsdaten,
  • suspend-Funktionen, um Aktionen mit viewModelScope auszuführen.
Verwenden Sie ViewModels auf Bildschirmebene.
Dringend empfohlen

Verwenden Sie ViewModels nicht in wiederverwendbaren Elementen der Benutzeroberfläche. Sie sollten ViewModels in folgenden Ländern verwenden:

  • Zusammensetzbare Funktionen auf Bildschirmebene,
  • Aktivitäten/Fragmente in Ansichten,
  • Ziele oder Diagramme bei Verwendung der Jetpack-Navigation
Verwenden Sie einfache State Holder-Klassen in wiederverwendbaren UI-Komponenten.
Dringend empfohlen
Verwenden Sie einfache State Holder-Klassen, um Komplexität in wiederverwendbaren UI-Komponenten zu bewältigen. Auf diese Weise kann der Zustand extern hochgezogen und gesteuert werden.
AndroidViewModel darf nicht verwendet werden.
Empfohlen
Verwenden Sie die Klasse ViewModel, nicht AndroidViewModel. Die Klasse Application sollte nicht in ViewModel verwendet werden. Verschieben Sie die Abhängigkeit stattdessen in die Benutzeroberfläche oder die Datenschicht.
Stellt einen UI-Status bereit.
Empfohlen
ViewModels sollte Daten über eine einzige Property namens uiState für die Benutzeroberfläche bereitstellen. Wenn auf der Benutzeroberfläche mehrere, nicht zusammenhängende Daten angezeigt werden, kann die VM mehrere UI-Statusattribute verfügbar machen.
  • Sie sollten uiState zu StateFlow machen.
  • Sie sollten die uiState mit dem Operator stateIn und der WhileSubscribed(5000)-Richtlinie (Beispiel) erstellen, wenn die Daten als Datenstrom aus anderen Ebenen der Hierarchie übertragen werden.
  • In einfacheren Fällen, in denen keine Datenströme aus der Datenschicht stammen, ist es akzeptabel, eine MutableStateFlow zu verwenden, die als unveränderliche StateFlow dargestellt wird (Beispiel).
  • Sie können ${Screen}UiState als Datenklasse verwenden, die Daten, Fehler und Ladesignale enthalten kann. Diese Klasse kann auch eine versiegelte Klasse sein, wenn die verschiedenen Zustände ausgeschlossen sind.

Das folgende Snippet veranschaulicht, wie der UI-Status aus einem ViewModel 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 der Android-App Lebenszyklus:

Empfehlung Beschreibung
Überschreiben Sie keine Lebenszyklusmethoden in Aktivitäten oder Fragmenten.
Dringend empfohlen
Überschreiben Sie keine Lebenszyklusmethoden wie onResume in Aktivitäten oder Fragmenten. Verwenden Sie stattdessen LifecycleObserver. Wenn die App Arbeiten ausführen muss, wenn der Lebenszyklus einen bestimmten Lifecycle.State erreicht, verwenden Sie die repeatOnLifecycle API.

Das folgende Snippet beschreibt, wie Vorgänge bei einer bestimmten Lebenszyklusstatus:

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

Beim Verwalten von Abhängigkeiten sollten Sie mehrere Best Practices beachten. zwischen Komponenten:

Empfehlung Beschreibung
Verwenden Sie eine Abhängigkeitsinjektion.
Dringend empfohlen
Orientieren Sie sich an den Best Practices für Abhängigkeitseinschleusung, vor allem für die Konstruktor-Einschleusung, sofern möglich.
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 häufig verwendet wird.
Verwenden Sie Hilt.
Empfohlen
Verwenden Sie Hilt oder manuelle Abhängigkeitsinjektion in einfachen Apps. Verwende Hilt, wenn dein Projekt komplex genug ist. Beispiele: <ph type="x-smartling-placeholder">
    </ph>
  • Mehrere Bildschirme mit ViewModels-Integration
  • WorkManager-Nutzung – Integration
  • Ausweitung der Nutzung von Navigation, wie z. B. ViewModels, die sich auf das Navigationsdiagramm beziehen – Integration.

Testen

Hier einige Best Practices für Tests:

Empfehlung Beschreibung
Wissen, was Sie testen sollten
Dringend empfohlen

Sofern das Projekt nicht so einfach ist wie eine Hello World-App, sollten Sie zumindest Folgendes testen:

  • Einheitentest von ViewModels, einschließlich Abläufen.
  • Unittest-Datenschichtentitäten. Das sind Repositories und Datenquellen.
  • UI-Navigationstests, die als Regressionstests in CI nützlich sind
Fälschungen sind lieber.
Dringend empfohlen
Weitere Informationen finden Sie in der Dokumentation zur Verwendung von Test-Doubles in der Android-Dokumentation.
Testen Sie StateFlows.
Dringend empfohlen
Beim Testen von StateFlow: <ph type="x-smartling-placeholder">

Weitere Informationen finden Sie im Leitfaden zu Tests in Android-DAC.

Modelle

Bei der Entwicklung von Modellen in Ihren Apps sollten Sie diese Best Practices beachten:

Empfehlung Beschreibung
Ein Modell pro Ebene in komplexen Apps erstellen
Empfohlen

In komplexen Apps können Sie neue Modelle in verschiedenen Ebenen oder Komponenten erstellen, wenn dies sinnvoll ist. Betrachten Sie die folgenden Beispiele:

  • Eine Remote-Datenquelle kann das Modell, das sie über das Netzwerk empfängt, einer einfacheren Klasse zuordnen, die nur die Daten enthält, die die App benötigt.
  • Repositories können DAO-Modelle mit den Informationen, die die UI-Ebene benötigt, einfacheren Datenklassen zuordnen.
  • ViewModel kann Datenschichtmodelle in UiState-Klassen einschließen.

Namenskonventionen

Beim Benennen Ihrer Codebasis sollten Sie die folgenden Best Practices beachten:

Empfehlung Beschreibung
Benennungsmethoden
Optional
Methoden sollten eine Verbformulierung sein. Beispiel: makePayment().
Benennungseigenschaften.
Optional
Attribute sollten eine Nominalphrase sein. 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 sein: getAuthorsStream(): Flow<List<Author>>
Implementierungen von Benennungsschnittstellen
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 sollten das Präfix Fake haben, wie in FakeAuthorsRepository.