0Pricing
Kotlin Academy · Lekcja

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

  1. StateFlow: gorący kontener stanu dla interfejsu
  2. SharedFlow: magistrale zdarzeń i zdarzenia jednorazowe
  3. Konwersja zimnego Flow na gorący za pomocą shareIn i stateIn
  4. Testowanie StateFlow i SharedFlow z Turbine
← Powrót do Kotlin Academy