StateFlow ve SharedFlow

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

StateFlow

StateFlow, geçerli ve yeni durum güncellemelerini toplayıcılarına yayan, durum bağlı olduğu, 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 mülküne yeni bir değer atayın.

Android'de StateFlow, gözlemlenebilir ve değişken bir durumu koruması gereken sınıflar için mükemmel bir seçimdir.

Kotlin akışlarındaki örnekleri takip ederek View'in kullanıcı arayüzü durum güncellemelerini dinleyebilmesi ve ekran durumunun yapılandırma değişikliklerinden etkilenmemesi için LatestNewsViewModel'ten bir StateFlow gösterilebilir.

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

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ıcaktır: Akıştan veri toplamak herhangi bir üretici kodunu tetiklemez. StateFlow her zaman etkindir ve bellekte bulunur. Yalnızca bir çöp toplama kökünden 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 gözlemlenebilir diğer sınıflarda da bulabilirsiniz.

View, diğer tüm akışlarda olduğu gibi StateFlow için 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 olarak dönüştürmek için stateIn ara operatörünü kullanın.

StateFlow, Flow ve LiveData

StateFlow ve LiveData benzerliklere sahiptir. Her ikisi de gözlemlenebilir veri tutucu sınıflarıdır ve uygulama mimarinizde kullanıldığında benzer bir kalıp izler.

Ancak StateFlow ve LiveData'nin farklı davrandığını unutmayın:

  • StateFlow, oluşturucuya bir başlangıç durumunun iletilmesini gerektirir. LiveData ise bunu gerektirmez.
  • LiveData.observe(), görüntüleme STOPPED durumuna geçtiğinde tüketicinin kaydını otomatik olarak siler. StateFlow veya başka bir akıştan veri toplama işlemi ise 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, sıcak bir akıştır. Akış toplandığı veya bir çöp toplama kökünden ona başka referanslar olduğu sürece bellekte kalır. shareIn operatörünü kullanarak soğuk akışları sıcak duruma getirebilirsiniz.

Kotlin akışlarında oluşturulan callbackFlow örneğini kullanarak, 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ışı gerektiği kadar uzun süre etkin tutmak için tüm tüketicilerden daha uzun süre etkin kalmalı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ışı, yayınlanan son öğeyi yeni bir toplayıcıya tekrar 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 yayın öncesi üreticiyi etkin tutar. SharingStarted.EagerlyÜretici akışını hemen başlatma veya SharingStarted.Lazilyilk abone göründükten sonra paylaşımı başlatıp akışı sonsuza kadar etkin tutma gibi başka başlangıç politikaları da vardır.

SharedFlow

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

shareIn kullanmadan SharedFlow oluşturabilirsiniz. Örneğin, tüm içeriğin düzenli olarak aynı anda yenilenmesi için uygulamanın geri kalanına onay işaretleri göndermek üzere bir SharedFlow kullanabilirsiniz. En son haberleri getirmenin yanı sıra kullanıcı bilgileri bölümünü favori konular koleksiyonuyla yenilemek de 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, bir SharedFlow gösterir. StateFlow ile olduğu gibi, öğeleri akışa göndermek için sınıfta MutableSharedFlow türüne sahip bir destekleyici mülk 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, daha önce yayınlanan bir dizi değeri yeni aboneler için yeniden göndermenize olanak tanır.
  • onBufferOverflow, arabelleğin gönderilecek öğelerle dolduğu durumlar için bir politika belirtmenize olanak tanır. Varsayılan değer BufferOverflow.SUSPEND'tır ve arayanın askıya alınmasına neden olur. Diğer seçenekler DROP_LATEST veya DROP_OLDEST'dur.

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

Ek akış kaynakları