SharedFlow کیا ہے؟ Android میں SharedFlow بمقابلہ 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 — ایک بار استعمال ہونے والے واقعات کے لیے hot flow: نئے سبسکرائبرز replay ترتیب کے بغیر پچھلی قدریں حاصل نہیں کرتے۔
  • MutableSharedFlow — واقعات بھیجنے کے لیے emit() اور tryEmit() طریقوں کے ساتھ قابل تبدیل ورژن۔
  • SharedFlow vs StateFlow: SharedFlow قدروں کو یکجا نہیں کرتا (متعدد قدروں کو بفر کر سکتا ہے)، ابتدائی قدر کی ضرورت نہیں، ایک بار استعمال ہونے والے واقعات کے لیے موزوں۔
  • replay — نئے سبسکرائبرز کو دوبارہ چلائے جانے والے تازہ ترین واقعات کی تعداد (پہلے سے طے شدہ 0)۔
  • extraBufferCapacity — emit() کو مسدود ہونے سے روکنے کے لیے replay کے علاوہ اضافی بفر۔

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 سبسکرائبرز کو ہم آہنگ کرنے کے لیے باریک دانے دار لاک کا استعمال کرتا ہے اور JetBrains ٹیسٹوں سے تصدیق شدہ کارکردگی میں تنزلی کے بغیر 1000+ بیک وقت سبسکرائبرز تک لکیری اسکیل ایبلٹی فراہم کرتا ہے۔

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 واقعات بھیجنے کے لیے emit() (suspend) اور tryEmit()(non-suspend) طریقوں کے ساتھ SharedFlow کا قابل تبدیل ورژن ہے۔ اگر بفر بھرا ہوا ہو اور onBufferOverflow = SUSPEND ہو تو emit() معطل ہو جاتا ہے۔ tryEmit() Boolean لوٹاتا ہے — واقعہ بفر میں کامیابی سے شامل ہوا یا نہیں۔

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 اس کام کے لیے مثالی ہے۔

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
// مختلف منظرناموں کے لیے ترتیبات:

// 1. ایک بار استعمال ہونے والے UI واقعات (نیویگیشن، ٹوسٹ)
val uiEvents = MutableSharedFlow<UiEvent>(
    replay = 0,
    extraBufferCapacity = 5,
    onBufferOverflow = BufferOverflow.DROP_OLDEST
)

// 2. حالت کی مطابقت پذیری کے لیے Replay بہاؤ (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 واقعات کو سبسکرائب کرکے نیویگیشن انجام دیتا ہے۔ اسکرین گھمانے پر کمانڈ دہرائی نہیں جاتی۔

// 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۔

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 سے Google کا ViewModel کے لیے تجویز کردہ پیٹرن ہے۔

اکثر پوچھے گئے سوالات

کیا SharedFlow کوئی واقعہ کھو سکتا ہے؟

ہاں، اگر بفر بھر جائے اور onBufferOverflow = DROP_OLDEST یا DROP_LATEST ہو۔ SharedFlow ہر واقعہ کی ترسیل کی ضمانت نہیں دیتا — یہ پیغام کی قطار (جیسے Channel) نہیں ہے۔ اگر تمام واقعات کی ضمانتی ترسیل درکار ہو تو نہ بھرنے والے بفر (UNLIMITED) والا Channel یا BroadcastChannel (متروک) استعمال کریں۔ UI واقعات کے لیے پرانے واقعات کا کھو جانا (مثال کے طور پر، پرانی نیویگیشن) متوقع رویہ ہے، خرابی نہیں۔

SharedFlow Channel سے کیسے مختلف ہے؟

Channel ایک FIFO قطار ہے جہاں ہر واقعہ صرف ایک سبسکرائبر کو پہنچایا جاتا ہے (نقطہ سے نقطہ)۔ SharedFlow ایک نشریات ہے: ہر واقعہ تمام فعال سبسکرائبرز کو پہنچایا جاتا ہے۔ SharedFlow BroadcastChannel (جو متروک ہے) کے قریب ہے اور «ایک سے کئی» منظرناموں کے لیے موزوں ہے۔ Channel «ایک سے ایک» کے لیے ہے (تھریڈ پولز، pipeline)۔ 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 کیسے Test کیا جائے؟

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 نتیجہ کے ساتھ non-suspend فنکشن۔
  • sealed class کے ساتھ UiEvent پیٹرن — ViewModel سے View میں ایک بار استعمال ہونے والے واقعات کی منتقلی کا تجویز کردہ طریقہ۔
  • SharedFlow ہر سبسکرائبر کو ترسیل کی ضمانت دیتا ہے، لیکن بفر بھرنے پر ہر واقعہ کی ترسیل کی ضمانت نہیں دیتا۔
  • ایک ViewModel میں SharedFlow + StateFlow کا مجموعہ — حالت اور واقعات کو الگ کرنے والے فن تعمیر کے لیے بہترین پیٹرن۔

ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے

IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔

پروجیکٹ پر بحث کریں

مزید پڑھیں