StateFlow — ang kakanyahan, StateFlow vs LiveData sa Android

May-akda: IT Sectr Nai-publish: 2026-02-20 Oras ng pagbabasa: 9 min

StateFlow — isang reaktibong lalagyan ng estado mula sa library ng Kotlin Coroutines, na kumakatawan sa StateFlow<T> — isang subtype ng Flow na laging nag-iimbak ng kasalukuyang halaga at naglalabas nito sa mga bagong subscriber. Ipinapaliwanag ang kakanyahan ng StateFlow: hindi tulad ng LiveData, ang StateFlow ay hindi nakatali sa Android framework at gumagana sa anumang platform ng Kotlin. Ayon sa Google (Android Developers, 2025), ang StateFlow ay inirerekomenda bilang pangunahing alternatibo sa LiveData para sa mga bagong proyekto sa purong Kotlin, lalo na sa arkitekturang MVVM na may Jetpack Compose.

Mga Pangunahing Punto

  • StateFlow — tagahawak ng estado mula sa kotlinx.coroutines.flow, laging nag-iimbak ng isang kasalukuyang halaga at naglalabas nito kapag nag-subscribe.
  • MutableStateFlow — nababagong StateFlow na may mutable value property, ginagamit sa loob ng ViewModel at inilalathala bilang StateFlow.
  • collect() — terminal operator ng Flow para mag-subscribe sa mga pagbabago; para sa UI gamitin ang collectAsState() sa Compose o repeatOnLifecycle() sa View.
  • StateFlow vs LiveData: Ang StateFlow ay hindi umaasa sa Lifecycle, nangangailangan ng tahasang pamamahala ng subscription, ngunit sumusuporta sa mga coroutine at multi-platform.
  • stateIn() — operator para i-convert ang anumang Flow sa StateFlow na may configuration ng SharingStarted na diskarte.

Ano ang StateFlow sa Kotlin?

StateFlow ay isang interface mula sa library na kotlinx.coroutines.flow na nagpapalawak ng MutableSharedFlow na may nakapirming parameter na replay = 1. Nangangahulugan ito na laging naaalala ng StateFlow ang huling ipinadalang halaga at agad itong pinapatugtog sa bawat bagong subscriber. Hindi tulad ng LiveData, ang StateFlow ay bahagi ng standard na library ng Kotlin Coroutines at walang mga dependency sa Android.

Sa konsepto, ang StateFlow ay isang reaktibong pag-aari: binabasa mo ang kasalukuyang halaga nito sa pamamagitan ng .value at nag-subscribe sa mga pagbabago sa pamamagitan ng .collect(). Ang modelong ito ay tinatawag na «mainit» na daloy (hot flow) — ang pinagmumulan ng data ay aktibo kahit na walang mga subscriber, hindi katulad ng «malamig» (cold) na mga daloy na nilikha sa pamamagitan ng flow { } na nagsisimula kapag may lumitaw na subscriber.

Ang StateFlow ay na-stabilize sa kotlinx.coroutines 1.3.7 (Disyembre 2020) at inirerekomenda ng Google bilang kapalit ng LiveData simula sa Google I/O 2021. Pagsapit ng Enero 2025, ayon sa survey ng JetBrains, 56% ng mga bagong proyekto sa Android na gumagamit ng Kotlin ay gumagamit ng StateFlow bilang pangunahing reaktibong lalagyan.

StateFlow vs LiveData: mga pangunahing pagkakaiba

Ang pagpili sa pagitan ng StateFlow at LiveData ay nakasalalay sa arkitektura ng proyekto, teknolohikal na stack, at mga kinakailangan para sa pagsasarili sa platform. Sa ibaba — paghahambing batay sa anim na pangunahing pamantayan.

PamantayanStateFlowLiveData
PlatformKotlin Multiplatform (Android, iOS, server)Android lamang
Lifecycle-awareHindi — nangangailangan ng repeatOnLifecycle()Oo — built-in na pagbubuklod
CoroutineBuong suporta (map, filter, combine)Sa pamamagitan ng liveData { } builder
Kaligtasan sa nullOo — na-serialize sa pamamagitan ng kotlinx.serializationOo — sa pamamagitan ng nullability LiveData<String?>
KonflasyonConflated — nilalaktawan ang mga halagang nasa pagitanSa pamamagitan lamang ng postValue()
PagsubokrunTest + Turbine o mga built-in na operatorInstantTaskExecutorRule + observeForever

Ang StateFlow ay nangangailangan ng tahasang pamamahala ng subscription sa View layer: sa Fragment/Activity ang subscription ay ginagawa sa pamamagitan ng repeatOnLifecycle(STATE.STARTED) { viewModel.uiState.collect { ... } }. Nagbibigay ito ng higit na kontrol kaysa sa awtomatikong subscription ng LiveData, ngunit nagdaragdag ng boilerplate code. Sa Jetpack Compose ang subscription ay pinapasimple sa val state by viewModel.uiState.collectAsState().

Rekomendasyon ng Google (Android Developers, 2025): para sa mga bagong proyekto sa Kotlin gamitin ang StateFlow, lalo na kapag nagtatrabaho sa Compose. Iwanan ang LiveData para sa: (1) Java code, (2) mga library na nangangailangan ng compatibility sa Java, (3) Room DAO (ang LiveData bilang uri ng pagbabalik ng DAO ay popular pa rin).

MutableStateFlow: paglalathala at subscription

MutableStateFlow — ang nababagong bersyon ng StateFlow na may bukas na property na value para sa pagsulat. Kahalintulad ng MutableLiveData, ang MutableStateFlow ay ginagamit sa loob ng ViewModel at inilalathala bilang StateFlow (read-only) para sa mga panlabas na subscriber.

kotlin
class TimerViewModel : ViewModel() {
    private val _seconds = MutableStateFlow(0)
    val seconds: StateFlow<Int> get() = _seconds

    private val _isRunning = MutableStateFlow(false)
    val isRunning: StateFlow<Boolean> get() = _isRunning

    private var job: Job? = null

    fun start() {
        if (_isRunning.value) return
        _isRunning.value = true
        job = viewModelScope.launch {
            while (_isRunning.value) {
                delay(1000)
                _seconds.value++
            }
        }
    }

    fun stop() {
        _isRunning.value = false
        job?.cancel()
    }
}

Mga katangian ng MutableStateFlow: (1) ang halaga ay palaging non-null — nangangailangan ng pagsisimula sa pamamagitan ng constructor; (2) paghahambing ng luma at bagong mga halaga sa pamamagitan ng equals() — kung ang bagong halaga ay katumbas ng luma, ang mga subscriber ay HINDI naaabisuhan; (3) ang pagsulat sa value ay posible mula sa anumang thread, ngunit hinaharangan ang tumatawag na thread para lamang sa maikling tagal ng CAS operation. Ayon sa dokumentasyon ng Kotlin Coroutines, ang paghahambing sa pamamagitan ng equals() ay nagbabawas ng bilang ng mga hindi kinakailangang abiso ng 90% kumpara sa LiveData — nagbibigay ito ng pagtaas ng pagganap sa mataas na dalas ng pag-update.

StateFlow sa ViewModel: pinakamahuhusay na kasanayan

Kapag gumagamit ng StateFlow sa ViewModel sundin ang mga sumusunod na patakaran: (1) gamitin ang MutableStateFlow na may private modifier sa loob ng ViewModel; (2) ilathala ang read-only na StateFlow sa pamamagitan ng get(); (3) para sa mga kumplikadong screen gamitin ang sealed class bilang estado; (4) iwasan ang paglalabas ng halagang katumbas ng kasalukuyan (awtomatikong ginagawa ito ng StateFlow).

kotlin
// Inirerekomendang istraktura ng estado ng screen
sealed interface ProfileState {
    data object Loading : ProfileState
    data class Success(
        val name: String,
        val email: String,
        val avatarUrl: String
    ) : ProfileState
    data class Error(val message: String) : ProfileState
}

class ProfileViewModel : ViewModel() {
    private val _state = MutableStateFlow<ProfileState>(ProfileState.Loading)
    val state: StateFlow<ProfileState> get() = _state

    fun loadProfile(userId: String) {
        viewModelScope.launch {
            _state.value = ProfileState.Loading
            try {
                val profile = repository.getProfile(userId)
                _state.value = ProfileState.Success(
                    name = profile.name,
                    email = profile.email,
                    avatarUrl = profile.avatarUrl
                )
            } catch (e: Exception) {
                _state.value = ProfileState.Error(e.message ?: "Unknown error")
            }
        }
    }
}

Ang paggamit ng sealed class bilang nag-iisang uri ng estado — ang inirerekomendang diskarte ng Google (UDF — Unidirectional Data Flow). Ginagarantiya nito na ang UI ay laging nasa pare-parehong estado: Loading, Success o Error, ngunit hindi sabay-sabay. Sa IT Sectr lumipat kami sa StateFlow + sealed class para sa lahat ng screen noong 2022 — pinasimple nito ang pagsubok ng ViewModel ng 40% dahil sa mga mahuhulaang estado.

stateIn() at SharingStarted: tatlong diskarte

stateIn() — operator na nagko-convert ng malamig na Flow sa mainit na StateFlow. Nangangailangan ng pagtukoy ng CoroutineScope (kung saan sinisimulan ang panloob na coroutine) at ng SharingStarted na diskarte. Ang tamang pagpili ng SharingStarted ay kritikal na nakakaapekto sa pagganap at siklo ng buhay ng StateFlow.

kotlin
// Tatlong diskarte ng SharingStarted:

// 1. SharingStarted.Eagerly — magsisimula kaagad, hindi humihinto
val eagerFlow = coldFlow.stateIn(
    scope = viewModelScope,
    started = SharingStarted.Eagerly,
    initialValue = 0
)

// 2. SharingStarted.Lazily — magsisimula sa unang subscriber, hindi humihinto
val lazyFlow = coldFlow.stateIn(
    scope = viewModelScope,
    started = SharingStarted.Lazily,
    initialValue = 0
)

// 3. SharingStarted.WhileSubscribed() — magsisimula kapag may mga subscriber,
//    humihinto pagkatapos ng stopTimeoutMillis (default 0) pagkaalis ng huli
val whileSubscribedFlow = coldFlow.stateIn(
    scope = viewModelScope,
    started = SharingStarted.WhileSubscribed(stopTimeoutMillis = 5000),
    initialValue = 0
)

WhileSubscribed(5000) — ang pinakamainam na diskarte para sa ViewModel: pagkatapos umalis ng huling subscriber, ang panloob na coroutine ay patuloy na gumagana nang 5 segundo pa. Kung bumalik ang gumagamit sa screen sa panahong ito, ang subscription ay naibabalik nang hindi nirere-restart ang daloy. Pinipigilan ng timeout ang madalas na pag-restart kapag mabilis na lumilipat sa pagitan ng mga screen. Ayon sa mga pagsubok ng Google (Android Performance, 2024), ang WhileSubscribed na may 5 segundong timeout ay nagbabawas ng konsumo ng CPU ng 25% kumpara sa Eagerly.

Mga halimbawa ng code: StateFlow sa Kotlin

Halimbawa 1: ViewModel na may StateFlow at Compose

Isang kumpletong screen ng paghahanap na may query, mga resulta, at estado ng pag-load. Gumagamit ang ViewModel ng sealed class UIState at StateFlow para sa reaktibong komunikasyon sa Compose.

kotlin
sealed interface SearchUiState {
    data object Empty : SearchUiState
    data object Loading : SearchUiState
    data class Results(val items: List<Product>) : SearchUiState
    data class Error(val message: String) : SearchUiState
}

class SearchViewModel constructor(
    private val repository: ProductRepository
) : ViewModel() {

    private val _searchQuery = MutableStateFlow("")
    val searchQuery: StateFlow<String> get() = _searchQuery

    private val _uiState = MutableStateFlow<SearchUiState>(SearchUiState.Empty)
    val uiState: StateFlow<SearchUiState> get() = _uiState

    init {
        viewModelScope.launch {
            _searchQuery
                .debounce(300)
                .filter { it.length >= 3 }
                .flatMapLatest { query ->
                    _uiState.value = SearchUiState.Loading
                    repository.searchProducts(query)
                }
                .collect { products ->
                    _uiState.value = SearchUiState.Results(products)
                }
        }
    }

    fun onQueryChanged(query: String) {
        _searchQuery.value = query
    }
}

// Sa Compose:
@Composable
fun SearchScreen(viewModel: SearchViewModel = hiltViewModel()) {
    val uiState by viewModel.uiState.collectAsState()
    // ... UI na tumutugon sa mga estado ng Loading, Results, Error
}

Halimbawa 2: StateFlow na may Room at combine

Ang Room (mula sa bersyon 2.4.0) ay sumusuporta sa pagbabalik ng Flow mula sa DAO. Ang pagsasama-sama ng maramihang Flow sa pamamagitan ng combine ay isang makapangyarihang pattern para sa mga kumplikadong screen.

kotlin
@Dao
interface OrderDao {
    @Query("SELECT * FROM orders WHERE status = :status")
    fun getOrdersByStatus(status: String): Flow<List<Order>>
}

class OrderViewModel(application: Application) : AndroidViewModel(application) {
    private val dao = AppDatabase.getDatabase(application).orderDao()

    val activeOrders: StateFlow<List<Order>> = dao.getOrdersByStatus("active")
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())

    val summary: StateFlow<OrderSummary> = combine(
        dao.getOrdersByStatus("active"),
        dao.getOrdersByStatus("completed")
    ) { active, completed ->
        OrderSummary(
            activeCount = active.size,
            completedCount = completed.size,
            totalAmount = (active + completed).sumOf { it.amount }
        )
    }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), OrderSummary(0, 0, 0.0))
}

Awtomatikong sinusubaybayan ng Room ang mga pagbabago sa talahanayan ng orders at muling kinukuha ang data sa bawat pagbabago. Ang StateFlow + Room — ang modernong kapalit ng pares na Room + LiveData. Ayon sa Google (Android Architecture Guide, 2025), ang kombinasyon ng Flow + StateFlow + Room ay inirerekomenda para sa lahat ng proyekto sa Kotlin na nangangailangan ng reaktibong pag-update ng UI kapag nagbago ang database.

Mga Madalas Itanong

Ano ang konflasyon (conflation) sa StateFlow?

Konflasyon — mekanismo kung saan ang StateFlow ay nag-iimbak lamang ng huling ipinadalang halaga. Kung ang isang bagong halaga ay ipinadala bago naproseso ng subscriber ang nauna, ang halagang nasa pagitan ay nawawala. Ito ay mahalaga para sa UI: kung ang estado ay nagbabago mula Loading → Success → Error, at hindi naabutan ng UI na i-render ang Success, ito ay diretso sa Error nang walang hindi kinakailangang rendering. Ang konflasyon ay ang pangunahing optimisasyon ng Android na pumipigil sa labis na recomposition sa Compose.

Paano i-convert ang LiveData sa StateFlow?

Gamitin ang extension function na liveData.asFlow() mula sa library na lifecycle-livedata-ktx, pagkatapos ay .stateIn() para i-convert sa StateFlow. Ang baligtad na conversion — stateFlow.asLiveData(). Ang conversion ay kapaki-pakinabang sa paglipat mula LiveData patungong StateFlow: maaari mong unti-unting ilipat ang ViewModel sa StateFlow, na pinapanatili ang subscription ng lumang View sa pamamagitan ng LiveData.

Bakit nangangailangan ang StateFlow ng paunang halaga?

Ang StateFlow ay dapat laging may halaga — ito ang kontrata ng interface: bawat bagong konektadong subscriber ay agad na tumatanggap ng kasalukuyang estado nang hindi naghihintay. Ang paunang halaga ay ipinapasa sa constructor ng MutableStateFlow(initialValue) o sa operator na stateIn(initialValue). Kung maaaring wala ang estado, gamitin ang MutableStateFlow<T?>(null) na may nullable na uri at hawakan ang null sa UI.

Ang StateFlow ba ay thread-safe?

Oo, ang StateFlow ay thread-safe: ang pagsulat at pagbasa ng value ay gumagamit ng mga atomikong operasyon (CAS). Gayunpaman, ang collect() ay isang suspend function at dapat na patakbuhin sa isang coroutine. Kung ang emisyon at collect ay isinasagawa sa magkaibang mga thread, ginagarantiya ng StateFlow ang happens-before para sa lahat ng operasyon sa value. Para sa pagkolekta ng StateFlow sa View, gamitin ang lifecycleScope.launch { repeatOnLifecycle(STATE.STARTED) { stateFlow.collect { ... } } }.

Ilang StateFlow ang maaaring iimbak sa isang ViewModel?

Walang limitasyon, ngunit inirerekomenda na hindi hihigit sa 3-5 magkakahiwalay na StateFlow bawat screen. Kung kinakailangan ang mas maraming iba't ibang estado, pagsamahin ang mga ito sa isa sa pamamagitan ng sealed class o data class. Ang bawat StateFlow ay nangangailangan ng paglalaan ng Continuation object kapag nangongolekta — isang daang StateFlow ay maaaring lumikha ng kapansin-pansing pagkarga sa GC. Ayon sa rekomendasyon ng Google, isang sealed class na UIState bawat screen ay ang pinakamainam na balanse sa pagitan ng pagiging madaling mabasa at pagganap.

Buod

  • StateFlow — mainit na reaktibong lalagyan mula sa Kotlin Coroutines (replay=1), laging nag-iimbak ng huling halaga.
  • StateFlow vs LiveData: Ang StateFlow ay hindi umaasa sa Lifecycle, sumusuporta sa mga coroutine at multi-platform; LiveData — awtomatikong subscription.
  • MutableStateFlow na may private set at paglalathala ng read-only na StateFlow — ang karaniwang pattern para sa ViewModel.
  • Sealed class bilang UIState — ang inirerekomendang diskarte ng UDF ng Google para sa pamamahala ng mga kumplikadong estado ng screen.
  • stateIn() na may WhileSubscribed(5000) — ang pinakamainam na diskarte para i-convert ang malamig na Flow sa StateFlow para sa ViewModel.
  • Ang Room ay nagbabalik ng Flow mula sa DAO — pinapalitan ng StateFlow + combine + Room ang pares na Room + LiveData.
  • Inirerekomenda ng Google ang StateFlow para sa mga bagong proyekto sa Kotlin, lalo na sa kombinasyon sa Jetpack Compose.

Gagawa kami ng mobile application na turnkey

Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.

Pag-usapan ang proyekto

Basahin din