Android Coden
Android 7 min lesen

Server-Sent Events im Android-Überblick

Server-Sent Events liefern Live-Updates per HTTP. Du lernst, wann SSE in Android sinnvoll ist und worauf du achten musst.

Server-Sent Events, kurz SSE, sind eine Technik für Live-Updates über HTTP. Du nutzt sie, wenn ein Server deiner App fortlaufend neue Informationen schicken soll: zum Beispiel Statusänderungen, Chat-Hinweise, Benachrichtigungen, Fortschritte oder neue Einträge in einem Feed. Für Android ist dabei nicht nur die Netzwerkverbindung interessant. Wichtig ist vor allem, wie du diesen Datenstrom sauber in deine Architektur einordnest, als Zustand für Compose verfügbar machst und trotzdem robust bleibst, wenn die Verbindung abbricht oder das Gerät offline ist.

Was ist das?

Server-Sent Events sind ein einseitiger Stream vom Server zur App. Die App öffnet eine normale HTTP-Verbindung, und der Server hält diese Verbindung offen. Statt eine einzelne Antwort zu schicken und die Verbindung sofort zu beenden, sendet der Server nach und nach kleine Textnachrichten. Jede Nachricht ist ein Event.

Das mentale Modell ist: Deine App fragt nicht ständig nach: „Gibt es etwas Neues?“ Stattdessen sagt sie: „Ich bin verbunden, schick mir neue Ereignisse, sobald sie entstehen.“ Das spart unnötige Polling-Anfragen und kann sich für viele Live-Funktionen natürlicher anfühlen.

SSE ist nicht dasselbe wie WebSockets. WebSockets sind bidirektional: Client und Server können dauerhaft in beide Richtungen Nachrichten senden. SSE ist einfacher: Der Server sendet, der Client hört zu. Wenn deine App nur Live-Updates empfangen muss, kann diese Einfachheit ein Vorteil sein. Wenn die App ständig Befehle an den Server schicken muss, passt oft ein anderes Protokoll besser.

Im Android-Kontext ist „Server-Sent Events Awareness“ zuerst eine Einordnungsfähigkeit. Du musst nicht sofort jede Netzwerkbibliothek im Detail kennen. Du solltest erkennen, wann ein Stream von Serverdaten gebraucht wird, welche Alternativen existieren, und wie du den Stream in Kotlin, Jetpack, Compose und die Data Layer übersetzt. Das Ziel ist nicht, die UI direkt mit einer HTTP-Verbindung zu verkabeln. Das Ziel ist ein klarer Datenfluss.

Typische Einsatzfälle sind Lieferstatus, Support-Queues, Dashboard-Werte, Transaktionszustände oder kollaborative Listen. Der gemeinsame Nenner: Die App soll zeitnah reagieren, ohne aktiv alle paar Sekunden eine REST-Abfrage auszulösen. Trotzdem bleibt SSE eine Netzwerkfunktion mit allen üblichen Android-Problemen: App-Lifecycle, Konnektivität, Akku, Fehlerbehandlung, Wiederverbindung und Datenkonsistenz.

Wie funktioniert es?

Technisch basiert SSE auf HTTP. Der Client stellt eine Anfrage an einen Endpunkt, der mit dem Content-Type text/event-stream antwortet. Danach bleibt die Verbindung offen. Der Server schreibt Events zeilenweise in die Antwort. Ein Event kann eine ID, einen Namen und Daten enthalten. Häufig interessiert dich in der App vor allem der Inhalt aus data.

Für Android solltest du SSE als Datenstrom betrachten. In Kotlin passt dafür oft Flow, weil ein Flow mehrere Werte über Zeit ausgeben kann. Ein Repository kann den SSE-Client kapseln und daraus Domain-Objekte oder UI-nahe Modelle erzeugen. Das ViewModel sammelt diese Werte ein, kombiniert sie bei Bedarf mit lokalen Daten und stellt daraus einen stabilen UI-Zustand bereit. Compose liest dann diesen Zustand und rendert die Oberfläche neu.

Die Schichten sollten dabei klar bleiben. Die Netzwerkdetails gehören in eine Datenquelle oder ein Repository. Das ViewModel kennt idealerweise keine HTTP-Header und keine Stream-Parsing-Regeln. Compose kennt keine Socket- oder Stream-Verbindung. Diese Trennung hilft dir beim Testen und verhindert, dass die UI ungewollt Netzwerklogik steuert.

SSE passt gut zu einem unidirektionalen Datenfluss. Ein Event kommt vom Server, wird im Repository verarbeitet, landet eventuell in einer lokalen Datenbank und wird dann als State an die UI geliefert. Gerade bei Offline-First-Architekturen ist dieser Punkt zentral. Ein Live-Stream sollte nicht deine einzige Wahrheit sein. Wenn ein Event wichtige App-Daten verändert, speichere die Veränderung lokal oder synchronisiere sie mit deiner lokalen Datenquelle. Dann kann die App auch nach Prozess-Neustart, kurzer Netzstörung oder Bildschirmrotation sinnvoll weiterarbeiten.

Einsteiger verwechseln Live-Updates oft mit dauerhaft sichtbarer UI. Das ist nicht dasselbe. Die App kann einen Stream abonnieren, während ein bestimmter Screen aktiv ist. Sie kann aber auch im Hintergrund andere Mechanismen brauchen, etwa WorkManager oder Push Notifications. SSE ist gut für aktive Sitzungen, in denen der Nutzer gerade eine Oberfläche geöffnet hat und Änderungen sehen soll. Für zuverlässige Hintergrundzustellung ist SSE in mobilen Apps meist nicht die passende Grundtechnik, weil Android Hintergrundarbeit begrenzt und Netzwerkverbindungen beendet werden können.

Auch Wiederverbindung gehört zum Konzept. Mobile Netze wechseln, Geräte gehen in den Ruhezustand, Server trennen inaktive Verbindungen. Deine App muss damit rechnen, dass der Stream endet. Gute Implementierungen behandeln das nicht als Sonderfall, sondern als normalen Zustand: verbunden, verbindend, getrennt, Fehler. Diese Zustände gehören in dein Modell, damit die UI sinnvoll reagieren kann.

In der Praxis

Stell dir eine App vor, die den Status einer Bestellung zeigt. Der Server sendet Events, wenn sich der Status von „angenommen“ zu „in Bearbeitung“ und später zu „unterwegs“ ändert. In der App modellierst du den Stream nicht als Callback in der Composable, sondern als Flow im Repository.

data class OrderStatus(
    val orderId: String,
    val label: String,
    val updatedAtMillis: Long
)

sealed interface StreamState {
    data object Connecting : StreamState
    data class Connected(val status: OrderStatus) : StreamState
    data class Disconnected(val reason: String? = null) : StreamState
}

interface OrderEventsRepository {
    fun observeOrderStatus(orderId: String): Flow<StreamState>
}

Eine konkrete Implementierung würde den SSE-Endpunkt öffnen, Textzeilen lesen, Events parsen und daraus OrderStatus erzeugen. Der wichtige Architekturpunkt ist: Nach außen sieht der Rest der App nur einen Flow. Dadurch kannst du die Implementierung später austauschen, zum Beispiel gegen Polling, WebSockets oder eine Fake-Quelle im Test.

Im ViewModel sammelst du den Flow lifecycle-bewusst ein und stellst UI-State bereit:

class OrderViewModel(
    private val repository: OrderEventsRepository,
    private val orderId: String
) : ViewModel() {

    val streamState: StateFlow<StreamState> =
        repository.observeOrderStatus(orderId)
            .stateIn(
                scope = viewModelScope,
                started = SharingStarted.WhileSubscribed(5_000),
                initialValue = StreamState.Connecting
            )
}

In Compose beobachtest du dann nur den Zustand:

@Composable
fun OrderStatusScreen(
    viewModel: OrderViewModel
) {
    val state by viewModel.streamState.collectAsStateWithLifecycle()

    when (val current = state) {
        StreamState.Connecting -> Text("Verbindung wird aufgebaut")
        is StreamState.Connected -> Text("Status: ${current.status.label}")
        is StreamState.Disconnected -> Text("Keine Live-Verbindung")
    }
}

Dieses Beispiel zeigt die wichtigste Praxisregel: Der Screen sammelt Zustand, aber er besitzt den Stream nicht. Die Composable startet keine eigene Endlosschleife, baut keine HTTP-Verbindung in LaunchedEffect zusammen und parst keine Serverdaten. So bleibt die UI austauschbar und testbar.

Eine typische Stolperfalle ist, jedes Event sofort direkt in Compose-State zu schreiben und dabei lokale Daten zu umgehen. Das wirkt am Anfang schnell, wird aber instabil. Wenn der Screen neu erstellt wird, ist der letzte Zustand vielleicht weg. Wenn ein Event doppelt kommt, ist die UI vielleicht inkonsistent. Wenn die App offline geht, weiß sie nicht, was zuletzt gültig war. Besser ist: Prüfe, ob das Event eine fachliche Änderung darstellt. Falls ja, aktualisiere deine lokale Quelle oder deinen Repository-State kontrolliert. Die UI rendert dann aus dieser Quelle.

Eine zweite Stolperfalle ist fehlendes Backoff bei Wiederverbindungen. Wenn die Verbindung fehlschlägt, sollte die App nicht sofort in einer engen Schleife neu verbinden. Das belastet Akku, Server und Netzwerk. Nutze eine klare Strategie: kurze Wartezeit, begrenzte Wiederholungen oder exponentielles Backoff, dazu ein sichtbarer Verbindungsstatus. Bei sicherheitsrelevanten Daten prüfst du außerdem, ob Events eine ID haben und ob du nach einem Reconnect verpasste Ereignisse nachladen musst.

Als Entscheidungsregel kannst du dir merken: Nutze SSE, wenn der Server viele kleine Updates an eine aktiv genutzte App-Oberfläche senden soll und die Kommunikation hauptsächlich Server zu Client läuft. Nutze klassisches REST oder Paging, wenn Daten nur gelegentlich aktualisiert werden. Prüfe WebSockets, wenn beide Seiten dauerhaft aktiv Nachrichten senden. Prüfe Push Notifications, wenn Updates den Nutzer erreichen sollen, obwohl die App nicht aktiv geöffnet ist.

Für Tests ist die Flow-Abstraktion hilfreich. Du kannst im Unit-Test ein Fake-Repository bauen, das gezielt Connecting, Connected und Disconnected emittiert. Dann prüfst du, ob das ViewModel die Zustände korrekt weitergibt und ob die UI alle Fälle sauber darstellt. Im Code-Review solltest du besonders darauf achten, ob Netzwerklogik in Composables gelandet ist, ob Verbindungsfehler sichtbar modelliert werden und ob wichtige Live-Daten nicht nur flüchtig im Speicher liegen.

Fazit

Server-Sent Events sind für Android dann relevant, wenn du Live-Updates als HTTP-Stream empfangen willst, ohne gleich ein bidirektionales Protokoll einzusetzen. Baue dir das richtige Modell: Der Server sendet Ereignisse, dein Repository übersetzt sie in einen Flow, das ViewModel formt daraus stabilen Zustand, und Compose rendert diesen Zustand. Prüfe dein Verständnis praktisch, indem du einen kleinen Fake-SSE-Flow schreibst, Verbindungsabbrüche simulierst und im Debugger beobachtest, ob deine UI zwischen „verbindet“, „verbunden“ und „getrennt“ sauber wechselt.

Quellen (4)
Redaktion

Geschrieben von

Redaktion

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