StateFlow: gorący kontener stanu dla interfejsu
Używaj StateFlow jako reaktywnego kontenera stanu i obserwuj go w ViewModelach.
StateFlow: gorący kontener stanu dla interfejsu to bezpłatna lekcja Kotlin Academy na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Kotlin Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Kotlin Academy zawiera 4 lekcji w sumie.
Czym jest StateFlow?
StateFlow to gorący obiekt Flow przechowujący stan, który zawsze ma wartość i emituje aktualizacje do wszystkich kolektorów. Zastępuje LiveData w nowoczesnych architekturach Kotlina.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val stateFlow = MutableStateFlow(0) // initial value
println("Current: ${stateFlow.value}")
stateFlow.value = 42
println("Updated: ${stateFlow.value}")
}MutableStateFlow a StateFlow
MutableStateFlow to zmienna implementacja używana wewnętrznie. Na zewnątrz należy udostępniać tylko do odczytu StateFlow, przez rzutowanie lub użycie .asStateFlow().
import kotlinx.coroutines.flow.*
class CounterViewModel {
private val _count = MutableStateFlow(0) // mutable internally
val count: StateFlow<Int> = _count.asStateFlow() // read-only externally
fun increment() { _count.value++ }
fun decrement() { _count.value-- }
}Zbieranie StateFlow
Należy zbierać StateFlow tak jak każdy inny Flow. Najnowsza wartość jest emitowana natychmiast po rozpoczęciu zbierania.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val state = MutableStateFlow("loading")
launch {
state.collect { println("State: $it") }
}
delay(50); state.value = "success"
delay(50); state.value = "idle"
delay(50)
coroutineContext.cancelChildren()
}StateFlow scala wartości
StateFlow emituje tylko wtedy, gdy wartość się zmienia. Jeśli ustawią Państwo tę samą wartość dwa razy, nastąpi tylko jedna emisja — kolejne identyczne wartości są scalane.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val flow = MutableStateFlow("A")
launch {
flow.collect { println("Received: $it") }
}
delay(50)
flow.value = "A" // no emission — same value
flow.value = "B" // emits
flow.value = "B" // no emission — same value
flow.value = "C" // emits
delay(50)
coroutineContext.cancelChildren()
}StateFlow w ViewModel
Standardowy wzorzec dla ViewModel: prywatny MutableStateFlow modyfikowany przez logikę biznesową oraz publiczny StateFlow obserwowany przez interfejs użytkownika.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
sealed class UiState { object Loading : UiState(); data class Success(val data: String) : UiState(); data class Error(val msg: String) : UiState() }
class MyViewModel(scope: CoroutineScope) {
private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
init {
scope.launch {
delay(100) // simulate load
_uiState.value = UiState.Success("Hello!")
}
}
}update() do atomowej modyfikacji
Należy używać update { }, aby atomowo aktualizować stan na podstawie bieżącej wartości — jest to ważne przy równoczesnych aktualizacjach.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val counter = MutableStateFlow(0)
List(100) {
launch(Dispatchers.Default) {
counter.update { it + 1 } // thread-safe atomic update
}
}.forEach { it.join() }
println("Counter: ${counter.value}") // 100
}compareAndSet do optymistycznych aktualizacji
compareAndSet(expect, update) ustawia nową wartość tylko wtedy, gdy bieżąca wartość jest zgodna z expect. Jest przydatne przy optymistycznej współbieżności.
import kotlinx.coroutines.flow.*
fun main() {
val state = MutableStateFlow("idle")
val changed = state.compareAndSet("idle", "loading")
println("Changed: $changed, Value: ${state.value}") // true, loading
val changed2 = state.compareAndSet("idle", "error") // won't change
println("Changed: $changed2, Value: ${state.value}") // false, loading
}StateFlow a LiveData
StateFlow działa bez cyklu życia Androida. LiveData uwzględnia cykl życia, ale działa wyłącznie na Androidzie. W przypadku współdzielonych ViewModels KMP należy preferować StateFlow.
import kotlinx.coroutines.flow.*
// StateFlow: pure Kotlin, works in commonMain
// val state = MutableStateFlow("value")
// LiveData: Android-only, lifecycle-aware
// val liveData = MutableLiveData("value")
// In Compose, collect StateFlow with:
// val state by viewModel.uiState.collectAsState()
fun main() { println("StateFlow: multiplatform; LiveData: Android-only") }stateIn: konwersja zimnego Flow do StateFlow
flow.stateIn(scope, started, initialValue) konwertuje zimny Flow na gorący StateFlow, współdzieląc jedną subskrypcję między wszystkimi kolektorami.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val cold = flow {
println("Fetching...")
delay(100)
emit("data")
}
val hot: StateFlow<String> = cold.stateIn(
scope = this,
started = SharingStarted.Lazily,
initialValue = "loading"
)
launch { hot.collect { println("A: $it") } }
launch { hot.collect { println("B: $it") } }
delay(200)
coroutineContext.cancelChildren()
}Strategie SharingStarted
Eagerly: uruchamia się natychmiast. Lazily: uruchamia się przy pierwszym kolektorze. WhileSubscribed(stopTimeout): zatrzymuje się, gdy nie ma kolektorów, i uruchamia ponownie, gdy któryś się zasubskrybuje.
import kotlinx.coroutines.flow.*
// Eagerly: upstream starts right away
// SharingStarted.Eagerly
// Lazily: waits for first subscriber
// SharingStarted.Lazily
// WhileSubscribed: stops 5s after last subscriber
// SharingStarted.WhileSubscribed(5000)
// Typical ViewModel usage:
// val uiState = repo.dataFlow.stateIn(viewModelScope, WhileSubscribed(5000), Loading)Obserwowanie stanu pochodnego
Nowy StateFlow można wyprowadzić z istniejącego za pomocą map + stateIn. Pochodny przepływ aktualizuje się za każdym razem, gdy zmieni się źródło.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val count = MutableStateFlow(5)
val doubled = count.map { it * 2 }.stateIn(this, SharingStarted.Eagerly, 10)
println(doubled.value) // 10
count.value = 7
delay(50)
println(doubled.value) // 14
coroutineContext.cancelChildren()
}Szybkie sprawdzenie
Co robi StateFlow, gdy ta sama wartość zostanie ustawiona dwa razy?
Podsumowanie
StateFlow to gorący obiekt Flow, który zawsze ma wartość i scala kolejne duplikaty. Należy używać wewnętrznie MutableStateFlow, a na zewnątrz udostępniać StateFlow. Do równoczesnych modyfikacji należy używać update(), a do konwertowania zimnych przepływów — stateIn().
Często zadawane pytania
Czy lekcja „StateFlow: gorący kontener stanu dla interfejsu” jest bezpłatna?
Tak — pełny tekst „StateFlow: gorący kontener stanu dla interfejsu” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Kotlin Academy, przejdź na CoddyKit PRO. Kurs Kotlin Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „StateFlow: gorący kontener stanu dla interfejsu”?
Używaj StateFlow jako reaktywnego kontenera stanu i obserwuj go w ViewModelach. Ćwiczysz Kotlin Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Kotlin Academy?
Nie wymagamy żadnego doświadczenia. Kotlin Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.
Ile czasu zajmuje lekcja „StateFlow: gorący kontener stanu dla interfejsu”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Kotlin Academy?
Tak. Każda lekcja Kotlin Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- StateFlow: gorący kontener stanu dla interfejsu
- SharedFlow: magistrale zdarzeń i zdarzenia jednorazowe
- Konwersja zimnego Flow na gorący za pomocą shareIn i stateIn
- Testowanie StateFlow i SharedFlow z Turbine