Wer sich mit Android beschäftigt, kennt sicherlich das altbekannte Problem der Datenverarbeitung in der Präsentationsschicht. Lange Zeit war LiveData die unangefochtene Nummer eins, doch mit dem Aufkommen von Kotlin-Coroutinen sind deutlich leistungsfähigere und flexiblere Werkzeuge entstanden. Die Rede ist von StateFlow und SharedFlow , die die natürliche Weiterentwicklung für die reaktive und effiziente Verwaltung von Datenflüssen darstellen.
Tatsächlich scheinen die Konzepte zwar auf den ersten Blick ähnlich, verfolgen aber jeweils einen anderen Zweck. Die Anzeige des Newsfeeds soll sich merken, wo dieser zuletzt angezeigt wurde, und gleichzeitig eine kurze Benachrichtigung für eine Toast-Nachricht senden, die nur einmal gelesen werden soll. Um Missverständnisse zu vermeiden, erklären wir im Folgenden die Funktionsweise dieser Hot-Streams und warum Sie LiveData in modernen Projekten besser nicht mehr verwenden sollten.
Das Konzept des Flusses: Kalt oder heiß?
Um StateFlow und SharedFlow zu verstehen, muss man zunächst den Unterschied zwischen einem kalten und einem heißen Flow begreifen. Ein Standard-Flow ist kalt , d. h. er ist wie ein Rezept: Es passiert nichts, bis ihn jemand ausführt (durch den Aufruf von `collect`). Das Problem dabei ist, dass bei drei Abonnenten der Code des Produzenten dreimal ausgeführt wird, was den Akku belasten oder redundante API-Anfragen verursachen kann.
Hier kommen Hot Streams ins Spiel. Sowohl StateFlow als auch SharedFlow sind strategisch Hot Streams , d. h. der Producer bleibt aktiv, unabhängig davon, ob jemand zuhört. Dadurch können mehrere Collector dieselbe Datenquelle nutzen , wodurch verhindert wird, dass die Anwendung dieselbe Arbeit wiederholt ausführt – ein entscheidender Faktor für die Systemstabilität.
StateFlow: Der ideale Ersatz für LiveData
StateFlow ist im Wesentlichen ein Zustandscontainer. Seine Hauptfunktion besteht darin, dass er stets einen aktuellen Wert speichert , der synchron über die `.value`-Eigenschaft abgefragt werden kann. Er eignet sich ideal zur Verwaltung des Zustands der Benutzeroberfläche, beispielsweise um festzustellen, ob ein Bildschirm geladen wird oder ob Profildaten erfolgreich aktualisiert wurden.
Im Gegensatz zu LiveData benötigt StateFlow einen Initialwert im Konstruktor, wodurch die Unsicherheit durch Nullzustände beim Start vermieden wird. Darüber hinaus implementiert es ein Verhalten zur Zusammenführung und Deduplizierung von Werten : Wird derselbe Wert zweimal hintereinander ausgegeben, ignoriert der Flow ihn einfach. Dies ist ideal, um unnötige Aktualisierungen der Benutzeroberfläche zu verhindern, wenn sich die Daten nicht geändert haben.
Um dies in einem ViewModel zu implementieren, empfiehlt sich die Verwendung einer privaten MutableStateFlow-Fallback-Eigenschaft , die als unveränderlicher StateFlow bereitgestellt wird. Dadurch wird sichergestellt, dass nur das ViewModel den Zustand ändern kann, der unidirektionale Datenfluss eingehalten und eine Manipulation der Daten durch die View verhindert wird.
SharedFlow: Der Ereignisbus für einzigartige Aktionen
StateFlow dient der Zustandsverwaltung, SharedFlow der Ereignisverwaltung. Stellen Sie sich vor, Sie möchten einen Fehlerdialog anzeigen oder zu einem anderen Bildschirm navigieren. Bei Verwendung von StateFlow würde das Drehen des Geräts dazu führen, dass der neue Collector den letzten Wert empfängt und das Ereignis erneut ausgelöst wird – ein schwerwiegender Fehler. SharedFlow behebt dieses Problem, da es mit einem Wiederholungswert von Null konfiguriert werden kann.
Dieser Ablauf ist deutlich flexibler und ermöglicht die Anpassung von Parametern wie dem Überlaufpuffer . Ist der Puffer voll, kann der Sender angehalten oder der älteste bzw. neueste Wert verworfen werden. Ein wesentlicher Vorteil von SharedFlow ist das Senden wiederholter Werte . Muss beispielsweise dieselbe Fehlermeldung „Verbindungsfehler“ dreimal gesendet werden, erledigt SharedFlow dies ohne Zögern, während StateFlow diese Meldungen herausfiltern würde.
Technischer Vergleich: StateFlow vs. SharedFlow vs. LiveData
Um Missverständnisse in technischen Vorstellungsgesprächen oder bei der Architekturplanung zu vermeiden, ist es wichtig, die kritischen Punkte zu beachten. LiveData ist einfach und verwaltet den Lebenszyklus automatisch , ist aber stark an Android gebunden. StateFlow und SharedFlow hingegen, die Teil der Kotlin-Coroutinen sind, sind plattformunabhängig , was Unit-Tests und die Erstellung sauberer Domänenschichten erheblich vereinfacht.
- StateFlow: Erzwingt einen Anfangswert, hat .value, dedupliziert Werte und ist ideal für UI-Zustände.
- Gemeinsamer FlussEs benötigt keinen Anfangswert, ist hinsichtlich der Wiedergabe konfigurierbar und ist die richtige Option für kurzlebige Ereignisse.
- LiveDatenEs benötigt keinen Anfangswert, berücksichtigt den Lebenszyklus, ist aber bei asynchronen Transformationen weniger leistungsfähig.
Die Bedeutung einer sicheren Ernte
Ein häufiger Fehler ist die Ausführung von `collect` innerhalb eines einfachen Coroutine-Scopes in der Activity. Dies ist gefährlich, da die Datenverarbeitung auch dann fortgesetzt wird, wenn die App im Hintergrund läuft , was zu Abstürzen oder übermäßiger CPU-Auslastung führen kann. Die moderne Lösung ist die Verwendung der API `repeatOnLifecycle(Lifecycle.State.STARTED)`.
Diese Funktion bewirkt, dass die Datenerfassung aktiviert wird, sobald die Ansicht sichtbar ist, und automatisch beendet wird, sobald der Status „STOPPED“ erreicht ist . Für Jetpack Compose wird die Verwendung von `collectAsStateWithLifecycle()` empfohlen , da diese Funktion die Logik nativ integriert und so einen effizienten Umgang der Geräteressourcen gewährleistet.
Umwandlung kalter Strömungen in warme Strömungen
Manchmal erhalten wir einen Datenfluss aus einer Datenbank (wie Room) oder einem Repository, der zunächst nicht aktiv ist. Um ihn aktiv zu machen, verwenden wir die Operatoren `stateIn` oder `shareIn` . Diese Operatoren benötigen einen `CoroutineScope` und eine Startstrategie. Die gebräuchlichste ist `SharingStarted.WhileSubscribed(5000)`.
Diese Konfiguration ist genial, da der Stream so lange aktiv bleibt, wie Abonnenten vorhanden sind. Wird der Bildschirm geschlossen, wartet das System fünf Sekunden, bevor der Stream gestoppt wird. Dies ist entscheidend, damit der Stream bei einer Bildschirmdrehung nicht zerstört und neu erstellt wird und somit unnötige Anfragen an den Server oder die lokale Datenbank vermieden werden.
Die Beherrschung dieser Werkzeuge erfordert das Verständnis, dass Zustände mithilfe von StateFlow persistent gespeichert und dedupliziert werden müssen, während einmalige Aktionen über SharedFlow abgewickelt werden. Durch die Kombination mit Lebenszyklusbewusstsein und der Transformation von Cold Flows mithilfe von stateIn erreichen wir eine robuste, vollständig reaktive Android-Architektur, die für die Performance auf mobilen Geräten im realen Einsatz optimiert ist. Teilen Sie diese Anleitung und helfen Sie anderen, mehr über dieses Thema zu erfahren.
