Empfehlungen für die Android-Architektur (Ansichten)

Konzepte und Jetpack Compose-Implementierung

Auf dieser Seite finden Sie mehrere Best Practices und Empfehlungen für die Architektur. 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.

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:

  • Sie sollten Repositories auch dann erstellen, wenn sie nur eine Datenquelle enthalten.
  • In kleinen Apps können Sie die Datenebenentypen in einem data-Paket oder -Modul platzieren.

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 Best Practices für ViewModels

Vorteile von ViewModels

Lebenszyklusbewusste Erfassung des UI-Zustands verwenden

Dringend empfohlen

Erfassen Sie den UI-Status über die UI mit dem entsprechenden lebenszyklusbezogenen Coroutine-Builder repeatOnLifecycle.

Weitere Informationen zu repeatOnLifecycle

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

Verwenden Sie eine App mit nur einer Aktivität.

Empfohlen

Verwenden Sie Navigationsfragmente, um zwischen Bildschirmen zu wechseln und Deeplinks zu Ihrer App zu erstellen, wenn Ihre App mehrere Bildschirme hat.

Im folgenden Snippet wird beschrieben, wie der UI-Zustand auf lebenszyklusbewusste Weise erfasst wird:

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
                }
            }
        }
    }
}

ViewModel

ViewModels sind für die Bereitstellung des UI-Zustands 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 sollten keine Referenz auf einen Lifecycle-bezogenen Typ enthalten. Übergeben Sie Activity, Fragment, Context oder Resources nicht als Abhängigkeit. Wenn etwas ein Context im ViewModel benötigt, sollten Sie genau prüfen, ob es sich in der richtigen Ebene befindet.

Verwenden Sie Coroutines und Flows.

Dringend empfohlen

Das ViewModel interagiert mit den Daten- oder Domänenebenen über:

  • Kotlin-Flows zum Empfangen von Anwendungsdaten,
  • suspend-Funktionen zum Ausführen von Aktionen mit viewModelScope.

ViewModels auf Bildschirmebene verwenden

Dringend empfohlen

Verwenden Sie ViewModels nicht in wiederverwendbaren UI-Elementen. ViewModels sollten in folgenden Fällen verwendet werden:

  • Aktivitäten/Fragmente in Ansichten
  • Ziele oder Diagramme bei Verwendung von Jetpack Navigation.

Verwenden Sie nicht AndroidViewModel.

Dringend 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 UI- oder Datenebene.

UI-Zustand bereitstellen

Empfohlen

ViewModels sollten Daten über eine einzelne Property namens uiState für die Benutzeroberfläche bereitstellen. Wenn auf der Benutzeroberfläche mehrere unabhängige Daten angezeigt werden, kann das ViewModel mehrere UI-Status-Properties bereitstellen.

  • Machen Sie uiState zu einem StateFlow.
  • Sie sollten uiState mit dem Operator stateIn und der Richtlinie WhileSubscribed(5000) (Beispiel) erstellen, wenn die Daten als Datenstream aus anderen Ebenen der Hierarchie stammen.
  • In einfacheren Fällen ohne Datenstreams aus der Datenschicht ist es akzeptabel, ein MutableStateFlow zu verwenden, das als unveränderliches StateFlow verfügbar gemacht wird.
  • Sie können ${Screen}UiState als Datenklasse auswählen, die Daten, Fehler und Ladesignale enthalten kann. Diese Klasse könnte auch eine sealed class sein, wenn sich die verschiedenen Status gegenseitig ausschließen.

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

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 Fragmenten.

Dringend empfohlen

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

Das folgende Snippet zeigt, wie Sie Vorgänge in einem bestimmten Lebenszyklusstatus ausführen:

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) {
                // ...
            }
        }
    }
}