StateFlow — Android'de StateFlow vs LiveData Karşılaştırması

Yazar: IT Sectr Yayınlanma: 2026-02-20 Okuma süresi: 9 dk

StateFlow — Kotlin Coroutines kütüphanesinden reaktif durum konteyneri olup, StateFlow<T> — her zaman güncel değeri tutan ve yeni abonelere bunu ileten Flow alt türüdür. StateFlow'un özünü anlatıyoruz: LiveData'dan farklı olarak StateFlow, Android çerçevesine bağlı değildir ve herhangi bir Kotlin platformunda çalışır. Google'a göre (Android Developers, 2025), StateFlow, özellikle Jetpack Compose ile MVVM mimarisinde, saf Kotlin ile yeni projeler için LiveData'ya ana alternatif olarak önerilmektedir.

Ana Noktalar

  • StateFlow — kotlinx.coroutines.flow'dan durum tutucu, her zaman tek bir güncel değeri tutar ve abonelikte bunu iletir.
  • MutableStateFlow — mutable value property'sine sahip değiştirilebilir StateFlow, ViewModel içinde kullanılır ve StateFlow olarak yayınlanır.
  • collect() — değişikliklere abone olmak için Flow terminal operatörü; UI için Compose'da collectAsState() veya View'da repeatOnLifecycle() kullanılır.
  • StateFlow vs LiveData: StateFlow Lifecycle'a bağlı değildir, açık abonelik yönetimi gerektirir, ancak coroutine'leri ve çoklu platform desteğini destekler.
  • stateIn() — herhangi bir Flow'u StateFlow'a dönüştüren operatör, SharingStarted stratejisi yapılandırması ile.

Kotlin'de StateFlow Nedir?

StateFlow — kotlinx.coroutines.flow kütüphanesinden, sabit replay = 1 parametresiyle MutableSharedFlow'u genişleten bir arayüzdür. Bu, StateFlow'un her zaman gönderilen son değeri hatırladığı ve her yeni aboneye hemen ilettiği anlamına gelir. LiveData'dan farklı olarak StateFlow, Kotlin Coroutines standart kütüphanesinin bir parçasıdır ve Android'e bağımlılığı yoktur.

Kavramsal olarak StateFlow reaktif bir özelliktir: geçerli değerini .value ile okur ve değişikliklere .collect() ile abone olursunuz. Bu modele "sıcak" akış (hot flow) denir — veri kaynağı, abonelerin varlığından bağımsız olarak aktiftir; flow { } ile oluşturulan ve bir abone göründüğünde başlatılan "soğuk" (cold) akışların aksine.

StateFlow, kotlinx.coroutines 1.3.7'de (Aralık 2020) stabilize edilmiş ve Google tarafından Google I/O 2021'den itibaren LiveData'nın yerine önerilmiştir. Ocak 2025 itibarıyla JetBrains anketine göre, yeni Kotlin Android projelerinin %56'sı ana reaktif konteyner olarak StateFlow kullanmaktadır.

StateFlow vs LiveData: Temel Farklar

StateFlow ve LiveData arasındaki seçim, proje mimarisine, teknoloji yığınına ve platform bağımsızlığı gereksinimlerine bağlıdır. Aşağıda altı temel kritere göre karşılaştırma bulunmaktadır.

KriterStateFlowLiveData
PlatformKotlin Multiplatform (Android, iOS, sunucu)Sadece Android
Lifecycle-awareHayır — repeatOnLifecycle() gerekliEvet — yerleşik bağlantı
Coroutine'lerTam destek (map, filter, combine)liveData { } builder aracılığıyla
Null-güvenliğiEvet — kotlinx.serialization ile serileştirilirEvet — LiveData<String?> nullability ile
ConflationConflated — ara değerleri atlarSadece postValue() ile
Test etmerunTest + Turbine veya yerleşik operatörlerInstantTaskExecutorRule + observeForever

StateFlow, View katmanında açık abonelik yönetimi gerektirir: Fragment/Activity'de abonelik repeatOnLifecycle(STATE.STARTED) { viewModel.uiState.collect { ... } } ile yapılır. Bu, LiveData'nın otomatik aboneliğinden daha fazla kontrol sağlar, ancak kalıp kod ekler. Jetpack Compose'da abonelik val state by viewModel.uiState.collectAsState() ile basitleşir.

Google önerisi (Android Developers, 2025): Kotlin ile yeni projelerde, özellikle Compose ile çalışırken StateFlow kullanın. LiveData'yı şunlar için bırakın: (1) Java kodu, (2) Java ile uyumluluk gerektiren kütüphaneler, (3) Room DAO (DAO dönüş türü olarak LiveData hala popülerdir).

MutableStateFlow: Yayınlama ve Abonelik

MutableStateFlow — yazma için açık value property'sine sahip değiştirilebilir StateFlow sürümüdür. MutableLiveData'ya benzer şekilde, MutableStateFlow ViewModel içinde kullanılır ve harici aboneler için StateFlow (salt okunur) olarak yayınlanır.

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()
    }
}

MutableStateFlow özellikleri: (1) değer her zaman null değildir — kurucu aracılığıyla başlatma gerektirir; (2) eski ve yeni değerlerin equals() ile karşılaştırılması — yeni değer eskiyle eşitse, abonelere bildirim yapılmaz; (3) value'ya yazma herhangi bir akıştan mümkündür, ancak çağıran akışı yalnızca CAS işleminin kısa süresi boyunca bloklar. Kotlin Coroutines belgelerine göre, equals() ile karşılaştırma, LiveData'ya kıyasla gereksiz bildirimlerin sayısını %90 oranında azaltır — bu, yüksek güncelleme sıklığında performans artışı sağlar.

ViewModel'de StateFlow: En İyi Uygulamalar

ViewModel'de StateFlow kullanırken şu kurallara uyun: (1) ViewModel içinde MutableStateFlow'u private değiştirici ile kullanın; (2) salt okunur StateFlow'u get() aracılığıyla yayınlayın; (3) karmaşık ekranlar için durum olarak sealed class kullanın; (4) mevcut değere eşit bir değer göndermekten kaçının (StateFlow bunu otomatik olarak yapar).

kotlin
// Önerilen ekran durumu yapısı
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")
            }
        }
    }
}

Tek tip durum olarak sealed class kullanımı, Google tarafından önerilen yaklaşımdır (UDF — Unidirectional Data Flow). UI'nın her zaman tutarlı bir durumda olmasını garanti eder: Loading, Success veya Error, ancak aynı anda değil. IT Sectr'da 2022'de tüm ekranlar için StateFlow + sealed class'a geçtik — bu, öngörülebilir durumlar sayesinde ViewModel testini %40 oranında basitleştirdi.

stateIn() ve SharingStarted: Üç Strateji

stateIn() — soğuk Flow'u sıcak StateFlow'a dönüştüren operatör. CoroutineScope (dahili coroutine'in başlatıldığı yer) ve SharingStarted stratejisinin belirtilmesini gerektirir. Doğru SharingStarted seçimi, StateFlow'un performansını ve yaşam döngüsünü kritik şekilde etkiler.

kotlin
// Üç SharingStarted stratejisi:

// 1. SharingStarted.Eagerly — hemen başlatılır, durdurulmaz
val eagerFlow = coldFlow.stateIn(
    scope = viewModelScope,
    started = SharingStarted.Eagerly,
    initialValue = 0
)

// 2. SharingStarted.Lazily — ilk abonede başlatılır, durdurulmaz
val lazyFlow = coldFlow.stateIn(
    scope = viewModelScope,
    started = SharingStarted.Lazily,
    initialValue = 0
)

// 3. SharingStarted.WhileSubscribed() — aboneler varken başlatılır,
//    son abone ayrıldıktan sonra stopTimeoutMillis (varsayılan 0) ile durdurulur
val whileSubscribedFlow = coldFlow.stateIn(
    scope = viewModelScope,
    started = SharingStarted.WhileSubscribed(stopTimeoutMillis = 5000),
    initialValue = 0
)

WhileSubscribed(5000) — ViewModel için en uygun strateji: son abone ayrıldıktan sonra dahili coroutine 5 saniye daha çalışmaya devam eder. Kullanıcı bu süre içinde ekrana dönerse, abonelik akışı yeniden başlatmadan geri yüklenir. Zaman aşımı, ekranlar arasında hızlı geçişlerde sık yeniden başlatmaları önler. Google testlerine göre (Android Performance, 2024), 5 saniyelik zaman aşımlı WhileSubscribed, CPU tüketimini Eagerly'ye kıyasla %25 oranında azaltır.

Kod Örnekleri: Kotlin'de StateFlow

Örnek 1: StateFlow ve Compose ile ViewModel

Arama sorgusu, sonuçlar ve yükleme durumu ile tam teşekküllü arama ekranı. ViewModel, Compose ile reaktif iletişim için sealed class UIState ve StateFlow kullanır.

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
    }
}

// Compose'da:
@Composable
fun SearchScreen(viewModel: SearchViewModel = hiltViewModel()) {
    val uiState by viewModel.uiState.collectAsState()
    // ... Loading, Results, Error durumlarına tepki veren UI
}

Örnek 2: Room ve combine ile StateFlow

Room (sürüm 2.4.0'dan itibaren) DAO'dan Flow dönüşünü destekler. Birden çok Flow'u combine ile birleştirmek, karmaşık ekranlar için güçlü bir desendir.

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))
}

Room, orders tablosundaki değişiklikleri otomatik olarak izler ve herhangi bir değişiklikte verileri yeniden sorgular. StateFlow + Room — Room + LiveData ikilisinin modern yerine geçendir. Google'a göre (Android Architecture Guide, 2025), veritabanı değişikliklerinde reaktif UI güncellemesi gerektiren tüm Kotlin projelerinde Flow + StateFlow + Room ikilisi önerilmektedir.

Sıkça Sorulan Sorular

StateFlow'da conflasyon (conflation) nedir?

Conflasyon — StateFlow'un yalnızca gönderilen son değeri sakladığı mekanizmadır. Yeni bir değer, abone öncekini işlemeden gönderilirse, ara değer kaybolur. Bu UI için önemlidir: durum Loading → Success → Error olarak değişirse ve UI Success'i çizmeye fırsat bulamazsa, gereksiz render olmadan doğrudan Error'a geçer. Conflasyon, Compose'da gereksiz yeniden birleştirmeleri önleyen temel bir Android optimizasyonudur.

LiveData StateFlow'a nasıl dönüştürülür?

lifecycle-livedata-ktx kütüphanesinden liveData.asFlow() extension fonksiyonunu, ardından StateFlow'a dönüştürmek için .stateIn() kullanın. Ters dönüşüm — stateFlow.asLiveData(). Dönüşüm, LiveData'dan StateFlow'a geçişte faydalıdır: ViewModel'leri kademeli olarak StateFlow'a taşıyabilir, eski View'ın LiveData üzerinden aboneliğini bırakabilirsiniz.

StateFlow neden başlangıç değeri gerektirir?

StateFlow her zaman bir değere sahip olmalıdır — bu arayüzün sözleşmesidir: yeni bağlanan her abone, beklemeden mevcut durumu hemen alır. Başlangıç değeri MutableStateFlow(initialValue) kurucusuna veya stateIn(initialValue) operatörüne iletilir. Durum mevcut olmayabilirse, nullable tür ile MutableStateFlow<T?>(null) kullanın ve UI'da null'u işleyin.

StateFlow thread-safe midir?

Evet, StateFlow thread-safe'dir: value yazma ve okuma atomik işlemler (CAS) kullanır. Ancak collect() bir suspend fonksiyonudur ve bir coroutine içinde başlatılmalıdır. Emission ve collect farklı akışlarda yürütülürse, StateFlow tüm value işlemleri için happens-before garantisi verir. View'da StateFlow toplamak için lifecycleScope.launch { repeatOnLifecycle(STATE.STARTED) { stateFlow.collect { ... } } } kullanın.

Bir ViewModel'de kaç StateFlow saklanabilir?

Herhangi bir sınırlama yoktur, ancak ekran başına 3-5'ten fazla ayrı StateFlow önerilmez. Daha fazla farklı durum gerekiyorsa, bunları sealed class veya data class aracılığıyla birleştirin. Her StateFlow, toplama sırasında bir Continuation nesnesinin ayrılmasını gerektirir — yüz StateFlow, GC üzerinde gözle görülür bir yük oluşturabilir. Google önerisine göre, ekran başına bir sealed class UIState, okunabilirlik ve performans arasında optimal dengedir.

Özet

  • StateFlow — Kotlin Coroutines'den (replay=1) her zaman son değeri tutan sıcak reaktif konteyner.
  • StateFlow vs LiveData: StateFlow Lifecycle'a bağlı değildir, coroutine'leri ve çoklu platform desteğini destekler; LiveData — otomatik abonelik.
  • MutableStateFlow private set ile ve salt okunur StateFlow yayınlama — ViewModel için standart desen.
  • Sealed class UIState olarak — Google tarafından önerilen UDF yaklaşımı, karmaşık ekran durumlarını yönetmek için.
  • stateIn() ile WhileSubscribed(5000) — soğuk Flow'u ViewModel için StateFlow'a dönüştürmenin optimal stratejisi.
  • Room DAO'dan Flow döndürür — StateFlow + combine + Room, Room + LiveData ikilisinin yerini alır.
  • Google, özellikle Jetpack Compose ile birlikte yeni Kotlin projeleri için StateFlow'u önermektedir.

Anahtar teslim bir mobil uygulama geliştireceğiz

IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.

Projeyi tartış

Ayrıca okuyun