StateFlow और SharedFlow

StateFlow और SharedFlow, फ़्लो एपीआई हैं. इनकी मदद से, फ़्लो में स्टेटस अपडेट को बेहतर तरीके से भेजा जा सकता है. साथ ही, एक से ज़्यादा उपभोक्ताओं को वैल्यू भेजी जा सकती है.

StateFlow

StateFlow स्टेटर की निगरानी वाला ऐसा फ़्लो है जो अपने कलेक्टर को मौजूदा और नए राज्य अपडेट देता है. मौजूदा स्थिति की वैल्यू को, value प्रॉपर्टी से भी पढ़ा जा सकता है. स्टेटस को अपडेट करने और उसे फ़्लो में भेजने के लिए, MutableStateFlow क्लास की value प्रॉपर्टी को नई वैल्यू असाइन करें.

Android में, StateFlow उन क्लास के लिए बिलकुल सही है जिनके लिए मॉनिटर किए जा सकने वाले म्यूट किए जा सकने वाले स्टेटस को बनाए रखना ज़रूरी होता है.

Kotlin फ़्लो के उदाहरणों का पालन करके, StateFlow को LatestNewsViewModel से एक्सपोज़ किया जा सकता है, ताकि View, यूज़र इंटरफ़ेस (यूआई) की स्थिति के अपडेट को सुन सके और कॉन्फ़िगरेशन में बदलाव होने पर भी स्क्रीन की स्थिति को बनाए रख सके.

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 को अपडेट करने की ज़िम्मेदारी, प्रोड्यूसर की होती है. साथ ही, StateFlow से डेटा इकट्ठा करने वाली सभी क्लास, कंज्यूमर होती हैं. flow बिल्डर का इस्तेमाल करके बनाए गए कोल्ड फ़्लो के उलट, StateFlow हॉट होता है: फ़्लो से डेटा इकट्ठा करने पर कोई प्रोड्यूसर कोड ट्रिगर नहीं होता. StateFlow हमेशा चालू और मेमोरी में रहता है. यह सिर्फ़ तब गैर-ज़रूरी डेटा इकट्ठा करने की प्रोसेस के लिए उपलब्ध होता है, जब गैर-ज़रूरी डेटा इकट्ठा करने की प्रोसेस के रूट से इसका कोई दूसरा रेफ़रंस न हो.

जब कोई नया कंज्यूमर फ़्लो से डेटा इकट्ठा करना शुरू करता है, तो उसे स्ट्रीम की आखिरी स्थिति और उसके बाद की सभी स्थितियां मिलती हैं. आपको यह व्यवहार, LiveData जैसी अन्य क्लास में भी दिख सकता है.

View अन्य फ़्लो की तरह ही, StateFlow को सुनता है:

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

किसी भी फ़्लो को StateFlow में बदलने के लिए, इंटरमीडिएट ऑपरेटर stateIn का इस्तेमाल करें.

StateFlow, फ़्लो, और LiveData

StateFlow और LiveData में एक जैसी चीज़ें हैं. दोनों, डेटा होल्डर क्लास हैं और ऐप्लिकेशन के आर्किटेक्चर में इस्तेमाल किए जाने पर, दोनों एक जैसे पैटर्न का पालन करती हैं.

हालांकि, ध्यान दें कि StateFlow और LiveData अलग-अलग तरीके से काम करते हैं:

  • StateFlow के लिए, कंस्ट्रक्टर में शुरुआती स्थिति को पास करना ज़रूरी है, जबकि LiveData के लिए ऐसा करना ज़रूरी नहीं है.
  • व्यू के STOPPED राज्य में जाने पर, LiveData.observe() उसका रजिस्ट्रेशन अपने-आप रद्द कर देता है. वहीं, StateFlow या किसी अन्य फ़्लो से डेटा इकट्ठा करना अपने-आप बंद नहीं होता. वही व्यवहार पाने के लिए, आपको Lifecycle.repeatOnLifecycle ब्लॉक से फ़्लो इकट्ठा करना होगा.

shareIn का इस्तेमाल करके कोल्ड फ़्लो को गर्म करना

StateFlow एक हॉट फ़्लो है—यह तब तक मेमोरी में बना रहता है, जब तक फ़्लो इकट्ठा किया जाता है या जब तक इसका कोई अन्य रेफ़रंस, कूड़े के कलेक्शन के रूट से मौजूद होता है. shareIn ऑपरेटर का इस्तेमाल करके, कोल्ड फ़्लो को हॉट फ़्लो में बदला जा सकता है.

उदाहरण के लिए, Kotlin फ़्लो में बनाए गए callbackFlow का इस्तेमाल करके, हर कलेक्टर के लिए नया फ़्लो बनाने के बजाय, shareIn का इस्तेमाल करके Firestore से इकट्ठा किए गए डेटा को कलेक्टर के बीच शेयर किया जा सकता है. आपको इन परीक्षाओं में पास होना होगा:

  • CoroutineScope, जिसका इस्तेमाल फ़्लो शेयर करने के लिए किया जाता है. यह दायरा किसी भी उपभोक्ता की तुलना में ज़्यादा समय तक चल सकता है, ताकि हम शेयर किए जा रहे डेटा को लंबे समय तक इस्तेमाल कर सकें.
  • हर नए कलेक्टर को फिर से चलाने के लिए आइटम की संख्या.
  • सेशन शुरू होने के व्यवहार से जुड़ी नीति.
class NewsRemoteDataSource(...,
    private val externalScope: CoroutineScope,
) {
    val latestNews: Flow<List<ArticleHeadline>> = flow {
        ...
    }.shareIn(
        externalScope,
        replay = 1,
        started = SharingStarted.WhileSubscribed()
    )
}

इस उदाहरण में, latestNews फ़्लो तब तक चालू रहता है, जब तक externalScope के साथ कलेक्टर मौजूद रहता है. SharingStarted.WhileSubscribed() शुरू करने की नीति, अपस्ट्रीम प्रॉड्यूसर को तब तक चालू रखती है, जब तक उसके चैनल पर सदस्य मौजूद हों. शुरू करने के लिए, अन्य नीतियां भी उपलब्ध हैं. जैसे, SharingStarted.Eagerlyप्रोड्यूसर को तुरंत शुरू करने के लिए या SharingStarted.Lazilyपहला सदस्य जुड़ने के बाद शेयर करना शुरू करने के लिए और फ़्लो को हमेशा चालू रखना.

शेयर किया गयाफ़्लो

shareIn फ़ंक्शन से SharedFlow मिलता है, जो एक हॉट फ़्लो है. यह उन सभी कंज्यूमर को वैल्यू भेजता है जो इससे डेटा इकट्ठा करते हैं. SharedFlow, StateFlow का एक ऐसा वर्शन है जिसे ज़्यादातर मामलों में इस्तेमाल किया जा सकता है और जिसे आसानी से कॉन्फ़िगर किया जा सकता है.

shareIn का इस्तेमाल किए बिना भी SharedFlow बनाया जा सकता है. उदाहरण के लिए, SharedFlow का इस्तेमाल करके, ऐप्लिकेशन के बाकी हिस्सों को टिक भेजा जा सकता है, ताकि पूरा कॉन्टेंट एक ही समय पर समय-समय पर रीफ़्रेश हो जाए. उपयोगकर्ता के लिए, सबसे नई खबरें फ़ेच करने के अलावा, उसके पसंदीदा विषयों के कलेक्शन के साथ उपयोगकर्ता की जानकारी वाले सेक्शन को भी रीफ़्रेश किया जा सकता है. नीचे दिए गए कोड स्निपेट में, TickHandler एक SharedFlow को दिखाता है, ताकि अन्य क्लास को यह पता रहे कि कॉन्टेंट को कब रीफ़्रेश करना है. StateFlow की तरह ही, किसी क्लास में MutableSharedFlow टाइप की बैकिंग प्रॉपर्टी का इस्तेमाल करके, आइटम को फ़्लो में भेजें:

// 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 के काम करने के तरीके को पसंद के मुताबिक बनाने के लिए, ये तरीके अपनाएं:

  • replay की मदद से, नए सदस्यों के लिए पहले से जनरेट की गई वैल्यू फिर से भेजी जा सकती हैं.
  • onBufferOverflow की मदद से, आपको यह तय करने की सुविधा मिलती है कि बफ़र, भेजे जाने वाले आइटम से पूरा भरा हुआ है या नहीं. डिफ़ॉल्ट वैल्यू BufferOverflow.SUSPEND है, जिससे कॉलर को निलंबित कर दिया जाता है. अन्य विकल्प DROP_LATEST या DROP_OLDEST हैं.

MutableSharedFlow में एक subscriptionCount प्रॉपर्टी भी होती है, जिसमें कलेक्टर की संख्या मौजूद होती है, ताकि आप अपने कारोबार के लॉजिक को उसी हिसाब से ऑप्टिमाइज़ कर सकें. अगर आपको फ़्लो में भेजी गई नई जानकारी को फिर से नहीं चलाना है, तो MutableSharedFlow में resetReplayCache फ़ंक्शन भी शामिल होता है.

फ़्लो के बारे में अन्य संसाधन