Android Coden
Android 7 min lesen

Datenstrategie im Review

Du prüfst, ob Speicherung, Netzwerk und Sync zusammenpassen. So entsteht eine robuste Datenstrategie für Offline-Nutzung.

Data Mastery Review bedeutet: Du prüfst, ob deine App-Daten nicht nur irgendwo gespeichert, geladen und angezeigt werden, sondern ob Storage, Network und Offline-Verhalten als zusammenhängende Strategie funktionieren. Für Android ist das wichtig, weil echte Apps selten nur eine API abfragen. Sie zeigen Daten in Compose, speichern Zustände lokal, senden Änderungen später an einen Server und müssen auch bei schlechtem Netz nachvollziehbar bleiben.

Was ist das?

Data Mastery Review ist kein einzelnes Jetpack-API und keine neue Bibliothek. Es ist ein Kontrollpunkt in deiner Lernreise: Kannst du erklären, woher deine Daten kommen, wo sie gespeichert werden, wann sie aktualisiert werden und welche Schicht dafür verantwortlich ist? Wenn du diese Fragen sauber beantworten kannst, bewegst du dich vom reinen Feature-Bauen hin zu belastbarer App-Architektur.

Im Android-Kontext betrifft das vor allem die Datenschicht. Diese Schicht kapselt Datenquellen wie REST- oder GraphQL-APIs, lokale Datenbanken, DataStore, Dateien oder In-Memory-Caches. Die UI, etwa ein Compose-Screen, sollte nicht wissen müssen, ob ein Profil gerade aus Room, aus dem Netzwerk oder aus einer synchronisierten Warteschlange stammt. Sie braucht einen beobachtbaren Zustand und klare Aktionen.

Das mentale Modell für Anfänger lautet: Daten haben einen Weg durch deine App. Sie starten an einer Quelle, werden in ein Modell übersetzt, gespeichert, beobachtet, verändert und eventuell mit einem Backend abgeglichen. Je größer die App wird, desto teurer werden unklare Wege. Wenn jeder Screen selbst Netzwerkaufrufe startet, eigene Caches hält und Fehler anders behandelt, entstehen Bugs, die schwer zu testen sind.

Data Mastery Review verbindet deshalb drei Begriffe. Storage beschreibt die lokale Wahrheit deiner App, zum Beispiel eine Room-Datenbank. Network beschreibt externe Datenquellen und Server-Kommunikation. Offline beschreibt das Verhalten, wenn das Netzwerk langsam, teuer, instabil oder nicht verfügbar ist. Eine reife Datenstrategie entscheidet nicht erst beim Fehlerfall, was passieren soll. Sie ist von Anfang an so gebaut, dass lokale Daten, entfernte Daten und Synchronisation zusammenpassen.

Wie funktioniert es?

Die zentrale Idee ist eine klare Zuständigkeit. In modernen Android-Apps landet diese Verantwortung oft in Repositorys. Ein Repository bietet der restlichen App fachliche Operationen an, etwa observeTasks(), refreshTasks() oder completeTask(id). Dahinter kann es lokale DAOs, Netzwerkservices und Sync-Logik koordinieren. Von außen bleibt die Schnittstelle stabil.

Für Offline-First-Apps ist ein häufiges Muster: Die UI liest zuerst aus der lokalen Datenquelle. Netzwerkdaten aktualisieren diese lokale Quelle. Dadurch beobachtet Compose nicht direkt die API-Antwort, sondern einen Flow aus der Datenbank. Wenn ein Refresh gelingt, ändern sich die Tabellen, der Flow sendet neue Werte, das ViewModel aktualisiert seinen UI-State und Compose zeichnet neu. Das ergibt einen ruhigen Datenfluss, der auch ohne Netz sinnvoll bleibt.

Wichtig ist dabei die Frage nach der “Source of Truth”. Das ist die Datenquelle, der deine App für einen bestimmten Zustand vertraut. In vielen Offline-First-Szenarien ist die lokale Datenbank die Source of Truth für das, was die UI anzeigt. Der Server bleibt trotzdem maßgeblich für globale Wahrheit, Berechtigungen oder Konfliktauflösung. Deine App muss aber nicht bei jedem Rendern den Server fragen.

Synchronisation ergänzt dieses Modell. Sie beantwortet: Wann werden lokale Daten zum Server gesendet? Wann werden entfernte Änderungen lokal übernommen? Was passiert bei Konflikten? Für kleine Apps reicht oft ein manueller Refresh. Für produktive Apps kommen WorkManager, Retry-Strategien, Zeitstempel, Änderungsprotokolle oder serverseitige Versionsnummern ins Spiel. Das Data Mastery Review prüft nicht jedes Detail davon, aber es fragt, ob du die Richtung des Datenflusses begründen kannst.

In der täglichen Entwicklung zeigt sich das an vielen Stellen. Du schreibst ein ViewModel und entscheidest, ob es eine Repository-Funktion aufruft oder direkt Retrofit nutzt. Du baust einen Compose-Screen und überlegst, ob Ladezustand, leere Liste und Fehlerzustand aus einem einheitlichen UiState kommen. Du fügst eine “Speichern”-Aktion hinzu und musst klären, ob sie sofort lokal sichtbar wird oder erst nach Serverbestätigung. Du schreibst Tests und merkst, ob deine Datenschicht austauschbar ist oder ob Netzwerk, Datenbank und UI zu eng verbunden sind.

Eine brauchbare Datenstrategie ist außerdem beobachtbar. Du solltest Logs, Debugger, Datenbank-Inspector und Tests nutzen können, um den Weg eines Datensatzes zu verfolgen. Wenn du nicht sagen kannst, warum ein alter Wert in der UI steht, fehlt wahrscheinlich eine klare Regel für Cache, Refresh oder Sync.

In der Praxis

Stell dir eine Aufgaben-App vor. Sie soll Aufgaben anzeigen, auch wenn das Gerät offline ist. Neue Daten vom Server sollen die lokale Liste aktualisieren. Der Compose-Screen soll davon nichts wissen. Dafür kannst du ein Repository so schneiden, dass es die lokale Datenbank beobachtet und Refresh getrennt anbietet.

class TaskRepository(
    private val taskDao: TaskDao,
    private val api: TaskApi,
    private val clock: Clock
) {
    fun observeTasks(): Flow<List<Task>> {
        return taskDao.observeAll()
            .map { entities -> entities.map { it.toDomain() } }
    }

    suspend fun refreshTasks() {
        val remoteTasks = api.getTasks()
        val entities = remoteTasks.map { it.toEntity(syncedAt = clock.now()) }
        taskDao.replaceAll(entities)
    }

    suspend fun completeTask(id: String) {
        taskDao.markCompleted(id, pendingSync = true)

        try {
            api.completeTask(id)
            taskDao.markSynced(id, syncedAt = clock.now())
        } catch (error: IOException) {
            // Die lokale Änderung bleibt sichtbar und wird später synchronisiert.
        }
    }
}

Das Beispiel ist bewusst kompakt. Die wichtige Regel ist nicht die genaue Implementierung, sondern die Richtung: Die UI beobachtet observeTasks(). Ein Refresh oder eine Aktion verändert die lokale Quelle. Dadurch bleibt der Screen stabil, auch wenn der Netzwerkaufruf fehlschlägt. Das ViewModel könnte observeTasks() in einen StateFlow für Compose umwandeln und refreshTasks() beim Start oder per Swipe-to-Refresh auslösen.

Eine typische Stolperfalle ist doppelter Zustand. Du lädst eine Liste per API, speicherst sie zusätzlich in Room, hältst aber im ViewModel noch eine eigene mutable Liste, die nicht aus Room abgeleitet ist. Bei Fehlern oder Rotation weißt du dann nicht mehr, welche Liste stimmt. Besser ist eine klare Regel: Für diesen Screen kommt die angezeigte Liste aus der lokalen Datenquelle. Netzwerk aktualisiert diese Quelle, aber umgeht sie nicht.

Eine zweite Stolperfalle ist falscher Optimismus. Wenn du lokale Änderungen sofort anzeigst, brauchst du eine Markierung wie pendingSync, syncError oder lastSyncedAt. Sonst sieht der Nutzer eine erledigte Aufgabe, obwohl der Server die Änderung später ablehnt. Offline-First heißt nicht, Fehler zu verstecken. Es heißt, lokale Arbeit zu ermöglichen und den Synchronisationsstatus ehrlich zu behandeln.

Als Entscheidungsregel kannst du dir merken: Wenn Daten länger als einen Screen-Lebenszyklus relevant sind, gehören sie nicht nur in remember oder in ein ViewModel-Feld. Prüfe, ob sie in eine lokale Datenquelle, in DataStore oder in eine klar begrenzte Cache-Schicht gehören. UI-State wie ein geöffneter Dialog ist etwas anderes als fachliche Daten wie Aufgaben, Profile oder Entwürfe.

Für Tests ist diese Trennung hilfreich. Du kannst ein Fake-DAO und eine Fake-API verwenden und prüfen, ob refreshTasks() die lokalen Daten ersetzt. Du kannst testen, ob completeTask() bei Netzwerkfehlern pendingSync setzt. In einem Code-Review solltest du genau solche Fragen stellen: Greift die UI direkt auf Network-Code zu? Gibt es eine Source of Truth? Ist Offline-Verhalten definiert oder nur zufällig? Werden Fehler und Ladezustände als Teil des Datenmodells oder UI-State sichtbar?

Auch Debugging wird dadurch konkreter. Setze einen Breakpoint im Repository und verfolge eine Änderung vom Button-Klick bis zur Datenbank. Öffne den Database Inspector und prüfe, ob die Werte dort dem entsprechen, was Compose anzeigt. Schalte das Netzwerk im Emulator ab und teste, ob bestehende Daten weiter erscheinen. Danach stellst du das Netzwerk wieder her und beobachtest, ob Sync oder Refresh nachvollziehbar laufen. Diese Übung zeigt schneller als jede Theorie, ob deine Architektur trägt.

Fazit

Data Mastery Review prüft, ob du Storage, Network und Offline-Verhalten als ein System verstehst. Du musst nicht jede Sync-Variante auswendig kennen, aber du solltest pro Feature erklären können, welche Datenquelle die UI beobachtet, wie Netzwerkdaten einfließen, wie lokale Änderungen behandelt werden und wie Fehler sichtbar werden. Nimm dir ein bestehendes kleines Projekt, zeichne den Datenfluss für einen Screen auf, teste ihn einmal ohne Netzwerk und prüfe im Code-Review, ob Repository, ViewModel und Compose jeweils ihre eigene Aufgabe behalten.

Quellen (2)
Redaktion

Geschrieben von

Redaktion

Das Redaktionsteam recherchiert und schreibt Artikel zu aktuellen Themen rund um Tech, Lifestyle und Ratgeber.