StateFlow ve SharedFlow

StateFlow ve SharedFlow, durum güncellemelerini en iyi şekilde yayınlayan ve birden fazla tüketiciye değer sunan akışları sağlayan Akış API'leridir.

StateFlow

StateFlow, mevcut ve yeni durum güncellemelerini toplayıcılara gönderen, eyalet sahibi ve gözlemlenebilir bir akıştır. Geçerli 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şken durumu koruması gereken sınıflar için idealdir.

Kotlin akışları ile ilgili örneklere göre, View öğesi kullanıcı arayüzü durumu güncellemelerini dinleyebilmek ve doğal olarak ekran durumunun yapılandırma değişikliklerini atlatmasını sağlamak için LatestNewsViewModel'den StateFlow açığa çıkabilir.

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

MutableStateFlow güncellemesinden sorumlu olan sınıf, yapımcıdır ve StateFlow öğesinden toplanan tüm sınıflar tüketicidir. flow oluşturucu kullanılarak oluşturulan soğuk akıştan farklı olarak StateFlow sıcaktır: Akıştan veri toplamak herhangi bir üretici kodunu tetiklemez. StateFlow her zaman etkindir ve bellekte yer alır. Ayrıca, yalnızca bir çöp toplama kökünden kendisine başka referans olmadığında çöp toplama için uygun hale gelir.

Yeni bir tüketici akıştan veri toplamaya başladığında akıştaki son durumu ve sonraki durumları alır. Bu davranışı LiveData gibi diğer gözlemlenebilir sınıflarda bulabilirsiniz.

View, diğer akışlarda olduğu gibi StateFlow dilini dinler:

class LatestNewsActivity : AppCompatActivity() {
    private val latestNewsViewModel = // 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. İkisi de gözlemlenebilir veri sahibi sınıflarıdır ve uygulama mimarinizde kullanıldıklarında benzer bir kalıp izlerler.

Ancak StateFlow ve LiveData değerlerinin farklı davrandığını unutmayın:

  • StateFlow, oluşturucuya bir başlangıç durumunun iletilmesini gerektirirken LiveData bunu yapmaz.
  • LiveData.observe(), görüntü STOPPED durumuna geçtiğinde tüketicinin kaydını otomatik olarak iptal eder. Bununla birlikte, StateFlow veya başka bir akıştan veri toplama işlemi otomatik olarak durmaz. Aynı davranışı elde etmek için bir Lifecycle.repeatOnLifecycle bloğundan akışı toplamanız gerekir.

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

StateFlow, sıcak bir akıştır. Akış alındığı veya bir atık toplama kökünden bu akışa başka referanslar bulunduğu sürece bellekte kalır. shareIn operatörünü kullanarak soğuk akışları sıcak hale getirebilirsiniz.

Kotlin akışlarında oluşturulan callbackFlow verisini örnek olarak kullanıp her toplayıcının yeni bir akış oluşturmasını istemek yerine, Firestore'dan alınan verileri shareIn kullanarak toplayıcılar arasında paylaşabilirsiniz. Aşağıdakileri geçmeniz gerekir:

  • Akışı paylaşmak için kullanılan bir CoroutineScope. Ortak akışın gereken süre boyunca geçerli kalması için bu kapsamın herhangi bir tüketiciden daha uzun sürmesi gerekir.
  • Her yeni koleksiyoncuya tekrar 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 tekrar oynatır ve externalScope yayında olduğu ve etkin toplayıcılar bulunduğu sürece etkin kalır. SharingStarted.WhileSubscribed() başlangıç politikası, etkin aboneler olduğu sürece yukarı akış yapımcısını etkin tutar. Yapımcıyı hemen başlatmak için SharingStarted.Eagerly veya ilk abone göründükten sonra paylaşıma başlamak ve akışı sonsuza kadar etkin tutmak için SharingStarted.Lazily gibi başka başlatma politikaları da mevcuttur.

Paylaşılan Akış

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

shareIn kullanmadan SharedFlow oluşturabilirsiniz. Örneğin, tüm içeriğin düzenli aralıklarla aynı anda yenilenmesi için uygulamanın geri kalanına onay işareti göndermek için SharedFlow kullanabilirsiniz. En son haberleri getirmenin yanı sıra, kullanıcı bilgileri bölümünü favori konu koleksiyonuyla yenilemek de isteyebilirsiniz. Aşağıdaki kod snippet'inde TickHandler, diğer sınıfların içeriğinin ne zaman yenileneceğini bilmesi için bir SharedFlow gösteriyor. StateFlow'de olduğu gibi, akışa öğe göndermek için sınıfta MutableSharedFlow türünde bir yedekleme özelliği 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<Event<String>> = _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 önceden yayınlanan bir dizi değeri yeniden göndermenize olanak tanır.
  • onBufferOverflow, arabelleğin gönderilecek öğelerle ne zaman dolduğunu belirleyen bir politika belirlemenize olanak tanır. Varsayılan değer BufferOverflow.SUSPEND, arayanı askıya alır. Diğer seçenekler: DROP_LATEST veya DROP_OLDEST.

MutableSharedFlow, aktif toplayıcı sayısını içeren bir subscriptionCount özelliğine de sahiptir. Böylece iş mantığınızı buna göre optimize edebilirsiniz. Akışa gönderilen en son bilgileri yeniden oynatmak istemezseniz MutableSharedFlow bir resetReplayCache işlevi de içerir.

Ek akış kaynakları