StateFlow ve SharedFlow

StateFlow ve SharedFlow, akışların durum güncellemelerini optimum şekilde yayınlamasını ve değerleri birden fazla tüketiciye yayınlamasını sağlayan Akış API'leridir.

StateFlow

StateFlow, mevcut ve yeni durum güncellemelerini toplayıcılarına yayan, durumu tutan bir gözlemlenebilir akıştır. Mevcut durum değeri, value özelliği aracılığıyla da okunabilir. Durumu güncellemek ve akışa göndermek için MutableStateFlow sınıfının value özelliğine yeni bir değer atayın.

Android'de, StateFlow, gözlemlenebilir değiştirilebilir bir durumu koruması gereken sınıflar için idealdir.

Kotlin akışlarındaki örnekleri takip ederek StateFlow, LatestNewsViewModel üzerinden kullanıma sunulabilir. Böylece View, kullanıcı arayüzü durumu güncellemelerini dinleyebilir ve ekran durumunun yapılandırma değişikliklerinden etkilenmemesini sağlayabilir.

class LatestNewsViewModel(
    private val newsRepository: NewsRepository
) : ViewModel() {

    // Backing property to avoid state updates from other classes
    private val _uiState = MutableStateFlow(LatestNewsUiState.Success(emptyList()))
    // The UI collects from this StateFlow to get its state updates
    val uiState: StateFlow<LatestNewsUiState> = _uiState

    init {
        viewModelScope.launch {
            newsRepository.favoriteLatestNews
                // Update UI with the latest favorite news
                // Writes to the value property of MutableStateFlow,
                // adding a new element to the flow and updating all
                // of its collectors
                .collect { favoriteNews ->
                    _uiState.value = LatestNewsUiState.Success(favoriteNews)
                }
        }
    }
}

// Represents different states for the LatestNews screen
sealed class LatestNewsUiState {
    data class Success(val news: List<ArticleHeadline>) : LatestNewsUiState()
    data class Error(val exception: Throwable) : LatestNewsUiState()
}

Bir MutableStateFlow öğesini güncellemekten sorumlu sınıf üreticidir ve StateFlow öğesinden veri toplayan tüm sınıflar tüketicidir. flow oluşturucu kullanılarak oluşturulan soğuk akışın aksine, StateFlow sıcak bir akıştır: Akıştan toplama işlemi herhangi bir üretici kodunu tetiklemez. Bir StateFlow her zaman aktiftir ve bellekte bulunur. Yalnızca çöp toplama kökünden başka referans kalmadığında çöp toplama için uygun hale gelir.

Akıştan veri toplamaya başlayan yeni bir tüketici, akıştaki son durumu ve sonraki tüm durumları alır. Bu davranışı LiveData gibi diğer gözlemlenebilir sınıflarda da görebilirsiniz.

View, diğer akışlarda olduğu gibi StateFlow için dinleme yapar:

class LatestNewsActivity : ComponentActivity() {
    private val latestNewsViewModel: LatestNewsViewModel = TODO() // getViewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        // ...
        // Start a coroutine in the lifecycle scope
        lifecycleScope.launch {
            // repeatOnLifecycle launches the block in a new coroutine every time the
            // lifecycle is in the STARTED state (or above) and cancels it when it's STOPPED.
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                // Trigger the flow and start listening for values.
                // Note that this happens when lifecycle is STARTED and stops
                // collecting when the lifecycle is STOPPED
                latestNewsViewModel.uiState.collect { uiState ->
                    // New value received
                    when (uiState) {
                        is LatestNewsUiState.Success -> showFavoriteNews(uiState.news)
                        is LatestNewsUiState.Error -> showError(uiState.exception)
                    }
                }
            }
        }
    }
}

Herhangi bir akışı StateFlow biçimine dönüştürmek için stateIn ara operatörünü kullanın.

StateFlow, Flow ve LiveData

StateFlow ve LiveData arasında benzerlikler var. Her ikisi de gözlemlenebilir veri sahibi sınıflarıdır ve uygulama mimarinizde kullanıldığında benzer bir kalıbı izler.

Ancak StateFlow ve LiveData sembollerinin farklı davrandığını unutmayın:

  • StateFlow, oluşturucuya başlangıç durumu iletilmesini gerektirirken LiveData gerektirmez.
  • LiveData.observe(), görünüm STOPPED durumuna geçtiğinde tüketiciyi otomatik olarak kaydını silerken StateFlow veya başka bir akıştan veri toplama işlemi otomatik olarak durdurulmaz. Aynı davranışı elde etmek için akışı bir Lifecycle.repeatOnLifecycle bloğundan toplamanız gerekir.

shareIn kullanarak soğuk akışları sıcak hale getirme

StateFlow, etkin bir akıştır. Akış toplandığı veya çöp toplama kökünden akışa yapılan diğer referanslar olduğu sürece bellekte kalır. shareIn operatörünü kullanarak soğuk akışları sıcak akışlara dönüştürebilirsiniz.

Kotlin akışlarında oluşturulan callbackFlow öğesini örnek olarak ele alırsak her toplayıcının yeni bir akış oluşturması yerine shareIn kullanarak Firestore'dan alınan verileri toplayıcılar arasında paylaşabilirsiniz. Aşağıdakileri iletmeniz gerekir:

  • Akışı paylaşmak için kullanılan bir CoroutineScope. Bu kapsam, paylaşılan akışın gerektiği kadar uzun süre devam etmesi için tüketicilerden daha uzun süre geçerli olmalıdır.
  • Her yeni toplayıcıya yeniden oynatılacak öğe sayısı.
  • Başlangıç davranışı politikası.

class NewsRemoteDataSource(
    private val externalScope: CoroutineScope
) {
    val latestNews: Flow<List<ArticleHeadline>> = flow {
        // ...
    }.shareIn(
        externalScope,
        replay = 1,
        started = SharingStarted.WhileSubscribed()
    )
}

Bu örnekte, latestNews akışı son yayınlanan öğeyi yeni bir toplayıcıya yeniden oynatır ve externalScope canlı olduğu ve etkin toplayıcılar olduğu sürece etkin kalır. SharingStarted.WhileSubscribed() Başlatma politikası, etkin aboneler olduğu sürece yukarı akış üreticisini etkin tutar. Diğer başlangıç politikaları da mevcuttur. Örneğin, SharingStarted.Eagerly üreticiyi hemen başlatmak veya SharingStarted.Lazily ilk abone göründükten sonra paylaşımı başlatmak ve akışı sonsuza kadar etkin tutmak için kullanılabilir.

SharedFlow

shareIn işlevi, kendisinden veri toplayan tüm tüketicilere değerler yayan bir sıcak akış olan SharedFlow döndürür. SharedFlow, StateFlow'nin son derece yapılandırılabilir bir genellemesidir.

shareIn kullanmadan SharedFlow oluşturabilirsiniz. Örneğin, tüm içeriklerin aynı anda düzenli olarak yenilenmesi için uygulamanın geri kalanına tik göndermek üzere SharedFlow kullanabilirsiniz. En son haberleri getirme dışında, kullanıcı bilgilerini en sevdiği konular koleksiyonuyla da yenilemek isteyebilirsiniz. Aşağıdaki kod snippet'inde, diğer sınıfların içeriğini ne zaman yenileyeceğini bilmesi için bir TickHandler, SharedFlow öğesini kullanıma sunar. StateFlow özelliğinde olduğu gibi, öğeleri akışa göndermek için bir sınıfta MutableSharedFlow türünde bir destekleyici özellik kullanın:

// Class that centralizes when the content of the app needs to be refreshed
class TickHandler(
    private val externalScope: CoroutineScope,
    private val tickIntervalMs: Long = 5000
) {
    // Backing property to avoid flow emissions from other classes
    private val _tickFlow = MutableSharedFlow<Unit>(replay = 0)
    val tickFlow: SharedFlow<Unit> = _tickFlow

    init {
        externalScope.launch {
            while (true) {
                _tickFlow.emit(Unit)
                delay(tickIntervalMs)
            }
        }
    }
}

class NewsRepository(
    // ...
    private val tickHandler: TickHandler,
    private val externalScope: CoroutineScope
) {
    init {
        externalScope.launch {
            // Listen for tick updates
            tickHandler.tickFlow.collect {
                refreshLatestNews()
            }
        }
    }

    suspend fun refreshLatestNews() { /* ... */ }
    // ...
}

SharedFlow davranışını aşağıdaki şekillerde özelleştirebilirsiniz:

  • replay, yeni aboneler için daha önce yayınlanan değerlerin bir kısmını yeniden göndermenize olanak tanır.
  • onBufferOverflow, gönderilecek öğelerle dolu olduğunda arabellek için bir politika belirtmenize olanak tanır. Varsayılan değer BufferOverflow.SUSPEND'dır ve bu değer, arayanın askıya alınmasına neden olur. Diğer seçenekler DROP_LATEST veya DROP_OLDEST'dir.

MutableSharedFlow ayrıca, işletme mantığınızı buna göre optimize edebilmeniz için etkin toplayıcıların sayısını içeren bir subscriptionCount özelliğine sahiptir. MutableSharedFlow, akışa gönderilen en son bilgileri tekrar oynatmak istemiyorsanız kullanabileceğiniz bir resetReplayCache işlevi de içerir.

Ek akış kaynakları