SharedFlow: यह क्या है, Android में SharedFlow vs StateFlow

लेखक: IT Sectr प्रकाशित: 2026-02-20 पढ़ने का समय: 9 मिनट

SharedFlow Kotlin Coroutines लाइब्रेरी से एक हॉट रिएक्टिव फ़्लो है, जो एकल-बार घटनाओं (one-shot events) के लिए अनुकूलित है जो स्क्रीन रोटेशन या सब्सक्राइबर पुनर्निर्माण पर दोहराई नहीं जानी चाहिए। हम दिखाते हैं कि SharedFlow StateFlow से कैसे अलग है: StateFlow के विपरीत, SharedFlow नए सब्सक्राइबर के लिए अंतिम मान संग्रहीत नहीं करता है और replay, extraBufferCapacity और onBufferOverflow के कॉन्फ़िगरेशन का समर्थन करता है। Google (Android Developers, 2025) के अनुसार, SharedFlow नेविगेशन कमांड, Snackbar संदेशों और अन्य घटनाओं के लिए अनुशंसित समाधान है जिन्हें ठीक एक बार संसाधित किया जाना चाहिए।

मुख्य बिंदु

  • SharedFlow — एकल-बार घटनाओं के लिए हॉट फ़्लो: नए सब्सक्राइबर replay कॉन्फ़िगरेशन के बिना पिछले मान प्राप्त नहीं करते हैं।
  • MutableSharedFlow — घटनाओं को भेजने के लिए emit() और tryEmit() विधियों वाला परिवर्तनशील संस्करण।
  • SharedFlow vs StateFlow: SharedFlow मानों को संकुचित नहीं करता (कई को बफ़र कर सकता है), प्रारंभिक मान की आवश्यकता नहीं है, एकल-बार घटनाओं के लिए उपयुक्त है।
  • replay — नए सब्सक्राइबर को दोहराई जाने वाली हाल की घटनाओं की संख्या (डिफ़ॉल्ट 0)।
  • extraBufferCapacity — replay से परे घटनाओं के लिए अतिरिक्त बफ़र, emit() को ब्लॉक होने से रोकता है।

Kotlin में SharedFlow क्या है?

SharedFlow kotlinx.coroutines.flow लाइब्रेरी से एक हॉट फ़्लो (hot flow) है, जो StateFlow के विपरीत, एक ही स्थिति से बंधा नहीं है और मनमाने सब्सक्राइबर को मनमानी संख्या में घटनाएँ उत्सर्जित कर सकता है। SharedFlow StateFlow के लिए आधार प्रकार है — StateFlow वास्तव में replay = 1 के साथ SharedFlow के माध्यम से कार्यान्वित किया गया है।

SharedFlow की मुख्य विशेषता यह है कि इसे अंतिम मान संग्रहीत करने की आवश्यकता नहीं है। डिफ़ॉल्ट रूप से (replay = 0), एक नया सब्सक्राइबर तब तक कुछ भी प्राप्त नहीं करता जब तक कोई नई घटना नहीं भेजी जाती। यह SharedFlow को उन परिदृश्यों के लिए आदर्श बनाता है जहाँ एक घटना को ठीक एक बार संसाधित किया जाना चाहिए: नेविगेशन, Snackbar, सिस्टम सूचनाएँ, QR कोड स्कैन परिणाम।

SharedFlow को kotlinx.coroutines 1.4.0 (नवंबर 2020) में StateFlow के साथ स्थिर किया गया था। Kotlin Coroutines दस्तावेज़ (2025) के अनुसार, SharedFlow सब्सक्राइबर सिंक्रनाइज़ेशन के लिए फ़ाइन-ग्रेन लॉकिंग का उपयोग करता है और बिना प्रदर्शन गिरावट के 1000+ समवर्ती सब्सक्राइबर तक रैखिक स्केलेबिलिटी प्रदान करता है, जिसकी पुष्टि JetBrains परीक्षणों से हुई है।

SharedFlow vs StateFlow: कब क्या उपयोग करें

SharedFlow और StateFlow के बीच चुनाव स्थानांतरित किए जा रहे डेटा की शब्दार्थ पर निर्भर करता है: स्थिति (StateFlow) या घटना (SharedFlow)। नीचे उदाहरणों के साथ स्पष्ट मानदंड दिए गए हैं।

मानदंडSharedFlowStateFlow
शब्दार्थएकल-बार घटनाएँ (नेविगेशन, टोस्ट, अलर्ट)UI स्थिति (सूची, लोडिंग, त्रुटि)
प्रारंभिक मानआवश्यक नहींआवश्यक
सब्सक्रिप्शन पर दोहरावकेवल यदि replay > 0हमेशा अंतिम मान
संकुचननहीं — घटनाएँ खोती नहीं (यदि बफ़र भरा नहीं है)हाँ — केवल नवीनतम संग्रहीत करता है
बफ़रिंगreplay + extraBufferCapacity के माध्यम से कॉन्फ़िगर करने योग्यकेवल 1 (replay=1 निश्चित)
उपयोगnavigationEvent, showSnackbar, openDialogitems, isLoading, uiState

सबसे सरल नियम: यदि डेटा स्क्रीन रोटेशन पर दिखाया जाना चाहिए — यह स्थिति है (StateFlow)। यदि स्क्रीन रोटेशन पर घटना दोहराई नहीं जानी चाहिए — यह एकल-बार घटना है (SharedFlow)। उदाहरण के लिए, त्रुटि संदेश वाला टोस्ट — SharedFlow: रोटेशन पर टोस्ट फिर से नहीं दिखना चाहिए। उत्पाद सूची — StateFlow: रोटेशन पर सूची स्क्रीन पर बनी रहनी चाहिए।

IT Sectr में हम SharedFlow का उपयोग करते हैं: नेविगेशन कमांड (स्क्रीन ट्रांज़िशन, डीप लिंक खोलना), UI घटनाएँ (Snackbar, AlertDialog), सिस्टम सूचनाएँ (बैकग्राउंड डेटा अपडेट, भुगतान परिणाम), विश्लेषण घटनाएँ (लॉगिंग, ट्रैकिंग)।

MutableSharedFlow: emit, tryEmit और बफ़रिंग

MutableSharedFlow SharedFlow का परिवर्तनशील संस्करण है जिसमें घटनाओं को भेजने के लिए emit() (suspend) और tryEmit() (गैर-suspend) विधियाँ हैं। emit() निलंबित हो जाता है यदि बफ़र भरा है और onBufferOverflow = SUSPEND है। tryEmit() एक Boolean लौटाता है जो दर्शाता है कि घटना सफलतापूर्वक बफ़र में जोड़ी गई या नहीं।

kotlin
class EventBus {
    private val _events = MutableSharedFlow<UiEvent>(
        replay = 0,
        extraBufferCapacity = 10,
        onBufferOverflow = BufferOverflow.DROP_OLDEST
    )
    val events: SharedFlow<UiEvent> get() = _events

    suspend fun sendEvent(event: UiEvent) {
        _events.emit(event)
    }

    fun trySendEvent(event: UiEvent): Boolean {
        return _events.tryEmit(event)
    }
}

sealed interface UiEvent {
    data class ShowSnackbar(val message: String) : UiEvent
    data class NavigateTo(val route: String) : UiEvent
    data class ShowDialog(val title: String, val message: String) : UiEvent
}

कंस्ट्रक्टर पैरामीटर अत्यंत महत्वपूर्ण हैं: replay = 0 सुनिश्चित करता है कि घटना किसी नए सब्सक्राइबर के लिए दोहराई न जाए; extraBufferCapacity = 10 UI के सब्सक्राइब करने से पहले तेज़ घटना उत्सर्जन के लिए बफ़र प्रदान करता है; DROP_OLDEST अतिप्रवाह रणनीति है: पुरानी घटनाएँ हटा दी जाती हैं, नई संरक्षित की जाती हैं। Kotlin Coroutines Performance (JetBrains, 2024) के अनुसार, extraBufferCapacity = 64 वाला SharedFlow बिना नुकसान के प्रति सेकंड 100,000 से अधिक घटनाओं को संसाधित करता है।

एकल-बार घटनाओं के लिए SharedFlow: Event पैटर्न

Event पैटर्न (या UiEvent) ViewModel से View में एकल-बार घटनाओं को पास करने का Google का अनुशंसित तरीका है। स्थिति (StateFlow) के विपरीत, एक घटना को ठीक एक बार संसाधित किया जाना चाहिए, और स्क्रीन रोटेशन पर इसे दोहराया नहीं जाना चाहिए। replay = 0 वाला SharedFlow इस कार्य के लिए आदर्श है।

kotlin
class CheckoutViewModel : ViewModel() {
    private val _uiState = MutableStateFlow<CheckoutState>(CheckoutState.Idle)
    val uiState: StateFlow<CheckoutState> get() = _uiState

    private val _event = MutableSharedFlow<CheckoutEvent>()
    val event: SharedFlow<CheckoutEvent> get() = _event

    fun placeOrder() {
        viewModelScope.launch {
            _uiState.value = CheckoutState.Loading
            try {
                val orderId = orderRepository.createOrder(cart)
                _uiState.value = CheckoutState.Success(orderId)
                _event.emit(CheckoutEvent.NavigateToOrderTracking(orderId))
            } catch (e: Exception) {
                _uiState.value = CheckoutState.Error(e.message)
                _event.emit(CheckoutEvent.ShowErrorSnackbar(e.message ?: "लेआउट त्रुटि"))
            }
        }
    }
}

sealed interface CheckoutEvent {
    data class NavigateToOrderTracking(val orderId: String) : CheckoutEvent
    data class ShowErrorSnackbar(val message: String) : CheckoutEvent
}

View (Activity/Fragment) में: घटनाओं की सब्सक्रिप्शन lifecycleScope में repeatOnLifecycle(STATE.STARTED) के साथ की जानी चाहिए। STARTED में प्रत्येक प्रवेश पर, सब्सक्रिप्शन फिर से बनाई जाती है, लेकिन घटना दोहराई नहीं जाती क्योंकि replay=0 वाले SharedFlow ने इसे पहले ही जारी कर दिया है। यह सुनिश्चित करता है कि ऑर्डर ट्रैकिंग स्क्रीन पर नेविगेशन केवल एक बार होता है, हर रोटेशन पर नहीं।

SharedFlow पैरामीटर: replay, extraBufferCapacity, onBufferOverflow

MutableSharedFlow कंस्ट्रक्टर तीन पैरामीटर स्वीकार करता है जो बफ़र व्यवहार निर्धारित करते हैं। गलत कॉन्फ़िगरेशन से घटनाओं का नुकसान या emit() ब्लॉकिंग हो सकती है।

पैरामीटरप्रकारडिफ़ॉल्टविवरण
replayInt0नए सब्सक्राइबर को दोहराई जाने वाली हाल की घटनाओं की संख्या। 0 = दोहराएँ नहीं, 1 = StateFlow जैसा
extraBufferCapacityInt0replay से परे अतिरिक्त बफ़र। घटनाएँ एक वृत्ताकार बफ़र में संग्रहीत होती हैं। 64 अधिकांश परिदृश्यों के लिए अनुशंसित सीमा है
onBufferOverflowBufferOverflowSUSPENDबफ़र भरने पर रणनीति: SUSPEND, DROP_OLDEST, DROP_LATEST
kotlin
// विभिन्न परिदृश्यों के लिए कॉन्फ़िगरेशन:

// 1. एकल-बार UI घटनाएँ (नेविगेशन, टोस्ट)
val uiEvents = MutableSharedFlow<UiEvent>(
    replay = 0,
    extraBufferCapacity = 5,
    onBufferOverflow = BufferOverflow.DROP_OLDEST
)

// 2. स्थिति सिंक्रनाइज़ेशन के लिए रीप्ले फ़्लो (StateFlow जैसा)
val stateLike = MutableSharedFlow<AppState>(
    replay = 1,
    extraBufferCapacity = 0
)

// 3. उच्च-आवृत्ति घटना उत्सर्जन (विश्लेषण, लॉग)
val analytics = MutableSharedFlow<AnalyticsEvent>(
    replay = 0,
    extraBufferCapacity = 100,
    onBufferOverflow = BufferOverflow.DROP_OLDEST
)

महत्वपूर्ण: extraBufferCapacity + replay = कुल बफ़र आकार। यदि emit() को सब्सक्राइबर द्वारा घटनाओं को संसाधित करने की तुलना में तेज़ी से कॉल किया जाता है, तो बफ़र भर जाता है और onBufferOverflow सक्रिय हो जाता है। UI घटनाओं के लिए, DROP_OLDEST एक सुरक्षित रणनीति है: पुरानी घटनाएँ (अप्रासंगिक नेविगेशन) नई के पक्ष में हटा दी जाती हैं। वित्तीय लेन-देन के लिए, SUSPEND का उपयोग करें — यह सुनिश्चित करता है कि प्रेषक को ब्लॉक करने की कीमत पर कोई घटना न खोए।

कोड उदाहरण: Kotlin में SharedFlow

उदाहरण 1: Jetpack Navigation के साथ नेविगेशन के लिए SharedFlow

नेविगेशन कमांड SharedFlow के लिए एक क्लासिक उपयोग मामला है। Fragment घटनाओं की सब्सक्राइब करता है और नेविगेशन करता है। स्क्रीन रोटेशन पर, कमांड दोहराई नहीं जाती।

kotlin
// ViewModel
class AuthViewModel : ViewModel() {
    private val _navEvent = MutableSharedFlow<NavEvent>()
    val navEvent: SharedFlow<NavEvent> get() = _navEvent

    fun onLoginSuccess() {
        viewModelScope.launch {
            _navEvent.emit(NavEvent.NavigateTo(NavRoutes.HOME))
        }
    }

    fun onLogout() {
        viewModelScope.launch {
            _navEvent.emit(NavEvent.NavigateTo(NavRoutes.LOGIN))
        }
    }
}

sealed interface NavEvent {
    data class NavigateTo(val route: String) : NavEvent
    data class NavigateBack(val popUpTo: String? = null) : NavEvent
}

// Fragment में:
viewLifecycleOwner.lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.navEvent.collect { navEvent ->
            when (navEvent) {
                is NavEvent.NavigateTo -> findNavController().navigate(navEvent.route)
                is NavEvent.NavigateBack -> findNavController().popBackStack()
            }
        }
    }
}

उदाहरण 2: Room और Flow ऑपरेटरों के साथ SharedFlow

एक जटिल परिदृश्य: UI के लिए StateFlow के साथ संयुक्त पृष्ठभूमि घटना सूचनाओं के लिए SharedFlow।

kotlin
class NotificationViewModel : ViewModel() {
    private val _toastMessage = MutableSharedFlow<String>()
    val toastMessage: SharedFlow<String> get() = _toastMessage

    private val _notifications = MutableStateFlow<List<Notification>>(emptyList())
    val notifications: StateFlow<List<Notification>> get() = _notifications

    init {
        viewModelScope.launch {
            notificationChannel
                .consumeAsFlow()
                .collect { notification ->
                    _notifications.value = _notifications.value + notification
                    _toastMessage.emit("नई सूचना: ${notification.title}")
                }
        }
    }

    fun dismissNotification(id: String) {
        _notifications.value = _notifications.value.filter { it.id != id }
    }

    fun markAllRead() {
        viewModelScope.launch {
            _notifications.value = _notifications.value.map { it.copy(isRead = true) }
            _toastMessage.emit("सभी सूचनाएँ पढ़ी गईं के रूप में चिह्नित")
        }
    }
}

इस उदाहरण में: StateFlow सूचना सूची संग्रहीत करता है (स्थिति — रोटेशन पर संरक्षित), SharedFlow टोस्ट संदेश उत्सर्जित करता है (एकल-बार घटनाएँ — रोटेशन पर दोहराई नहीं जातीं)। दो Flow प्रकारों का संयोजन 2022 से ViewModel के लिए Google का अनुशंसित पैटर्न है।

अक्सर पूछे जाने वाले प्रश्न

क्या SharedFlow कोई घटना खो सकता है?

हाँ, यदि बफ़र भरा हुआ है और onBufferOverflow = DROP_OLDEST या DROP_LATEST है। SharedFlow प्रत्येक घटना की डिलीवरी की गारंटी नहीं देता — यह संदेश कतार (जैसे Channel) नहीं है। यदि आपको सभी घटनाओं की गारंटीकृत डिलीवरी चाहिए, तो असीमित बफ़र (UNLIMITED) या BroadcastChannel (बहिष्कृत) वाले Channel का उपयोग करें। UI घटनाओं के लिए, पुरानी घटनाओं का खो जाना (जैसे, पुराना नेविगेशन) अपेक्षित व्यवहार है, बग नहीं।

SharedFlow Channel से कैसे अलग है?

Channel एक FIFO कतार है जहाँ प्रत्येक घटना ठीक एक सब्सक्राइबर को दी जाती है (बिंदु-से-बिंदु)। SharedFlow एक प्रसारण है: प्रत्येक घटना सभी सक्रिय सब्सक्राइबर को दी जाती है। SharedFlow BroadcastChannel (जो बहिष्कृत है) के करीब है और एक-से-अनेक परिदृश्यों के लिए उपयुक्त है। Channel एक-से-एक (थ्रेड पूल, पाइपलाइन) के लिए है। JetBrains की अनुशंसा के अनुसार, SharedFlow सभी नए प्रोजेक्ट्स में BroadcastChannel का प्रतिस्थापन है।

SharedFlow को थ्रेड-सेफ कैसे बनाएं?

SharedFlow पहले से ही थ्रेड-सेफ है — emit() और collect() ठीक से सिंक्रनाइज़ हैं। कई थ्रेड बिना लॉक के emit() कॉल कर सकते हैं, और सभी सक्रिय सब्सक्राइबर सही क्रम में घटनाएँ प्राप्त करते हैं। tryEmit() नॉन-ब्लॉकिंग है — यदि बफ़र भरा है तो false लौटाता है। उच्च-लोड सिस्टम के लिए, DROP_OLDEST के साथ tryEmit() का उपयोग करें — यह थ्रेड ब्लॉकिंग को रोकता है।

SharedFlow का उपयोग स्थिति के लिए क्यों नहीं किया जाता?

replay=1 के बिना SharedFlow अंतिम मान संग्रहीत नहीं करता — स्क्रीन रोटेशन पर एक नया सब्सक्राइबर वर्तमान स्थिति प्राप्त नहीं करेगा और UI खाली रहेगा। replay=1 के साथ, SharedFlow StateFlow जैसा व्यवहार करता है लेकिन equals() के माध्यम से तुलना अनुकूलन खो देता है, जिससे समान मान फिर से उत्सर्जित होने पर अनावश्यक सूचनाएँ होती हैं। StateFlow स्थिति के लिए सही विकल्प है; SharedFlow घटनाओं के लिए है।

SharedFlow का परीक्षण कैसे करें?

SharedFlow के परीक्षण के लिए, Turbine का उपयोग करें — Flow परीक्षण के लिए एक Kotlin लाइब्रेरी। Turbine प्रत्येक उत्सर्जन को अलग-अलग टाइमआउट और पूर्णता सत्यापन के साथ जाँचने की अनुमति देता है। उदाहरण: viewModel.event.test { assertEquals(UiEvent.ShowSnackbar("OK"), awaitItem()) }। आप अपेक्षित घटनाओं की संख्या निर्दिष्ट करते हुए runTest में .toList() का भी उपयोग कर सकते हैं।

सारांश

  • SharedFlow — अंतिम स्थिति से बंधी न होने वाली एकल-बार घटनाओं के लिए एक हॉट रिएक्टिव फ़्लो।
  • SharedFlow vs StateFlow: SharedFlow घटनाओं के लिए (नेविगेशन, टोस्ट, अलर्ट), StateFlow स्थिति के लिए (सूचियाँ, लोडिंग, त्रुटियाँ)।
  • MutableSharedFlow replay=0, extraBufferCapacity=5, DROP_OLDEST के साथ UI घटनाओं के लिए मानक कॉन्फ़िगरेशन है।
  • emit() — ब्लॉकिंग उत्सर्जन के लिए suspend फ़ंक्शन; tryEmit() — Boolean परिणाम के साथ गैर-suspend।
  • sealed class के साथ UiEvent पैटर्न ViewModel से View में एकल-बार घटनाओं को पास करने का Google का अनुशंसित तरीका है।
  • SharedFlow प्रत्येक सब्सक्राइबर को डिलीवरी की गारंटी देता है, लेकिन बफ़र अतिप्रवाह पर प्रत्येक घटना की डिलीवरी की गारंटी नहीं देता
  • एक ही ViewModel में SharedFlow + StateFlow का संयोजन स्थिति और घटनाओं को अलग करने वाली वास्तुकला के लिए इष्टतम पैटर्न है।

हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे

IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।

परियोजना पर चर्चा करें

यह भी पढ़ें