SharedFlow: nima, SharedFlow vs StateFlow Android-da

Muallif: IT Sectr Nashr etilgan: 2026-02-20 O'qish vaqti: 9 daq

SharedFlow — Kotlin Coroutines kutubxonasidan issiq reaktiv oqim, ekranning aylanishi yoki obunachining qayta yaratilishida takrorlanmasligi kerak bo'lgan bir martalik hodisalar (one-shot events) uchun optimallashtirilgan. SharedFlow StateFlow-dan nimasi bilan farq qilishini ko'rsatamiz: StateFlow-dan farqli o'laroq, SharedFlow yangi obunachilar uchun oxirgi qiymatni saqlamaydi va replay, extraBufferCapacity hamda onBufferOverflow konfiguratsiyasini qo'llab-quvvatlaydi. Google (Android Developers, 2025) ma'lumotlariga ko'ra, SharedFlow navigatsiya buyruqlari, Snackbar xabarlari va aynan bir marta qayta ishlanishi kerak bo'lgan boshqa hodisalar uchun tavsiya etilgan yechimdir.

Asosiy

  • SharedFlow — bir martalik hodisalar uchun hot flow: yangi obunachilar replay sozlanmasdan oldingi qiymatlarni olmaydi.
  • MutableSharedFlow — hodisalarni yuborish uchun emit() va tryEmit() metodlari bilan o'zgartiriladigan versiya.
  • SharedFlow vs StateFlow: SharedFlow qiymatlarni konflyatsiya qilmaydi (bir nechtasini buferlashi mumkin), boshlang'ich qiymat talab qilmaydi, bir martalik hodisalar uchun mos keladi.
  • replay — yangi obunachilarga takrorlanadigan oxirgi hodisalar soni (standart 0).
  • extraBufferCapacity — replay-dan tashqari hodisalar uchun qo'shimcha bufer, emit() bloklanishining oldini oladi.

Kotlin-da SharedFlow nima?

SharedFlow — kotlinx.coroutines.flow kutubxonasidan issiq oqim (hot flow) bo'lib, StateFlow-dan farqli o'laroq, bitta holatga bog'liq emas va istalgan miqdordagi hodisalarni istalgan obunachilarga emit qilishi mumkin. SharedFlow StateFlow uchun asosiy tur hisoblanadi — StateFlow replay = 1 bilan SharedFlow orqali amalga oshirilgan.

SharedFlow-ning asosiy xususiyati — oxirgi qiymatni saqlash shart emas. Standart holatda (replay = 0) yangi obunachi yangi hodisa yuborilgunga qadar hech narsa olmaydi. Bu SharedFlow-ni hodisa aynan bir marta qayta ishlanishi kerak bo'lgan stsenariylar uchun ideal qiladi: navigatsiya, Snackbar, tizim bildirishnomalari, QR kod skanerlash natijalari.

SharedFlow kotlinx.coroutines 1.4.0 (noyabr 2020) da StateFlow bilan birga barqarorlashtirilgan. Kotlin Coroutines (2025) hujjatlariga ko'ra, SharedFlow obunachilarni sinxronlash uchun nozik donali blokirovkadan foydalanadi va JetBrains testlari bilan tasdiqlangan ishlash degradatsiyasisiz 1000+ bir vaqtning o'zidagi obunachilargacha chiziqli masshtablanishni ta'minlaydi.

SharedFlow vs StateFlow: qachon nimani ishlatish

SharedFlow va StateFlow o'rtasidagi tanlov uzatiladigan ma'lumotlarning semantikasiga bog'liq: holat (StateFlow) yoki hodisa (SharedFlow). Quyida misollar bilan aniq mezonlar keltirilgan.

MezonSharedFlowStateFlow
SemantikaBir martalik hodisalar (navigatsiya, toast, ogohlantirish)UI holati (ro'yxat, yuklash, xato)
Boshlang'ich qiymatTalab qilinmaydiMajburiy
Obunada takrorlashFaqat replay > 0 bo'lsaHar doim oxirgi qiymat
KonflyatsiyaYo'q — hodisalar yo'qolmaydi (bufer to'lmasa)Ha — faqat oxirgisini saqlaydi
Buferlashreplay + extraBufferCapacity orqali sozlanadiFaqat 1 (replay=1 qat'iy)
FoydalanishnavigationEvent, showSnackbar, openDialogitems, isLoading, uiState

Eng oddiy qoida: agar ma'lumotlar ekran aylanganda ko'rsatilishi kerak bo'lsa — bu holat (StateFlow). Agar ekran aylanganda hodisa takrorlanmasligi kerak bo'lsa — bu bir martalik hodisa (SharedFlow). Masalan, „xato xabari bilan toast“ — SharedFlow: aylanishda toast qayta ko'rsatilmasligi kerak. „Mahsulotlar ro'yxati“ — StateFlow: aylanishda ro'yxat ekranda qolishi kerak.

IT Sectr da SharedFlow-ni ishlatamiz: navigatsiya buyruqlari (ekranga o'tish, deep link ochish), UI hodisalari (Snackbar, AlertDialog), tizim bildirishnomalari (fondagi ma'lumotlarni yangilash, to'lov natijasi), analitik hodisalar (loglash, kuzatish).

MutableSharedFlow: emit, tryEmit va buferlash

MutableSharedFlow — hodisalarni yuborish uchun emit() (suspend) va tryEmit() (non-suspend) metodlari bilan SharedFlow-ning o'zgartiriladigan versiyasi. emit() bufer to'la bo'lsa va onBufferOverflow = SUSPEND bo'lsa to'xtatiladi. tryEmit() Boolean qaytaradi — hodisa buferga muvaffaqiyatli qo'shilganmi.

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
}

Konstruktor parametrlari juda muhim: replay = 0 hodisaning yangi obunachi uchun takrorlanmasligini kafolatlaydi; extraBufferCapacity = 10 — UI obuna bo'lgunga qadar tez hodisa yuborish uchun bufer; DROP_OLDEST — to'lib toshish strategiyasi: eski hodisalar tashlanadi, yangilari saqlanadi. Kotlin Coroutines Performance (JetBrains, 2024) ma'lumotlariga ko'ra, extraBufferCapacity = 64 bo'lgan SharedFlow yo'qotishlarsiz soniyada 100 000 dan ortiq hodisani qayta ishlaydi.

Bir martalik hodisalar uchun SharedFlow: Event namunasi

Event (yoki UiEvent) namunasi — Google tomonidan ViewModel-dan View-ga bir martalik hodisalarni uzatish uchun tavsiya etilgan usul. Holatdan (StateFlow) farqli o'laroq, hodisa aynan bir marta qayta ishlanishi va ekran aylanganda takrorlanmasligi kerak. replay = 0 bo'lgan SharedFlow bu vazifa uchun idealdir.

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 ?: "Formatlash xatosi"))
            }
        }
    }
}

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

View-da (Activity/Fragment): hodisaga obuna lifecycleScope da repeatOnLifecycle(STATE.STARTED) bilan bajarilishi kerak. STARTED ga har bir kirishda obuna qayta yaratiladi, ammo hodisa takrorlanmaydi, chunki replay=0 bo'lgan SharedFlow uni allaqachon bo'shatgan. Bu buyurtmani kuzatish ekraniga navigatsiya har bir aylanishda emas, faqat bir marta sodir bo'lishini kafolatlaydi.

SharedFlow parametrlari: replay, extraBufferCapacity, onBufferOverflow

MutableSharedFlow konstruktori buferning xatti-harakatini belgilaydigan uchta parametrni qabul qiladi. Noto'g'ri sozlash hodisalarning yo'qolishiga yoki emit() bloklanishiga olib kelishi mumkin.

ParametrTurStandartTavsif
replayInt0Yangi obunachiga takrorlanadigan oxirgi hodisalar soni. 0 = takrorlanmaydi, 1 = StateFlow kabi
extraBufferCapacityInt0Replay-dan tashqari qo'shimcha bufer. Hodisalar halqali buferda saqlanadi. 64 — ko'p stsenariylar uchun tavsiya etilgan chegara
onBufferOverflowBufferOverflowSUSPENDBufer to'lganda strategiya: SUSPEND, DROP_OLDEST, DROP_LATEST
kotlin
// Turli stsenariylar uchun konfiguratsiyalar:

// 1. Bir martalik UI hodisalari (navigatsiya, toastlar)
val uiEvents = MutableSharedFlow<UiEvent>(
    replay = 0,
    extraBufferCapacity = 5,
    onBufferOverflow = BufferOverflow.DROP_OLDEST
)

// 2. Holat sinxronizatsiyasi uchun replay oqimi (StateFlow kabi)
val stateLike = MutableSharedFlow<AppState>(
    replay = 1,
    extraBufferCapacity = 0
)

// 3. Yuqori chastotali hodisa yuborish (analitika, loglar)
val analytics = MutableSharedFlow<AnalyticsEvent>(
    replay = 0,
    extraBufferCapacity = 100,
    onBufferOverflow = BufferOverflow.DROP_OLDEST
)

Muhim: extraBufferCapacity + replay = umumiy bufer hajmi. Agar emit() obunachining hodisani qayta ishlashidan tezroq chaqirilsa, bufer to'ladi va onBufferOverflow ishga tushadi. UI hodisalari uchun DROP_OLDEST — xavfsiz strategiya: eski hodisalar (endi dolzarb bo'lmagan navigatsiyalar) yangilari foydasiga tashlanadi. Moliyaviy tranzaktsiyalar uchun SUSPEND dan foydalaning — bu jo'natuvchini bloklash evaziga hech qanday hodisa yo'qolmasligini kafolatlaydi.

Kod misollari: Kotlin-da SharedFlow

1-misol: Jetpack Navigation bilan SharedFlow

Navigatsiya buyruqlari — SharedFlow uchun klassik foydalanish holati. Fragment hodisalarga obuna bo'ladi va navigatsiyani amalga oshiradi. Ekran aylanganda buyruq takrorlanmaydi.

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-da:
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-misol: Room va Flow operatorlari bilan SharedFlow

Murakkab stsenariy: UI uchun StateFlow bilan birlashtirgan holda fon hodisalari haqida bildirishnomalar uchun 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("Yangi bildirishnoma: ${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("Barcha bildirishnomalar o'qilgan deb belgilandi")
        }
    }
}

Ushbu misolda: StateFlow bildirishnomalar ro'yxatini saqlaydi (holat — aylanishda saqlanadi), SharedFlow toast xabarlarini emit qiladi (bir martalik hodisalar — aylanishda takrorlanmaydi). Ikki Flow turining kombinatsiyasi — 2022 yildan boshlab Google tomonidan ViewModel uchun tavsiya etilgan namunadir.

Tez-tez beriladigan savollar

SharedFlow hodisani yo'qotishi mumkinmi?

Ha, agar bufer to'la bo'lsa va onBufferOverflow = DROP_OLDEST yoki DROP_LATEST bo'lsa. SharedFlow har bir hodisaning yetkazilishini kafolatlamaydi — bu xabarlar navbati emas (Channel kabi). Agar barcha hodisalarning kafolatlangan yetkazilishi kerak bo'lsa, to'lib toshmaydigan buferli (UNLIMITED) Channel yoki BroadcastChannel (deprecated) dan foydalaning. UI hodisalari uchun eskirgan hodisalarning yo'qolishi (masalan, eski navigatsiya) — kutilgan xatti-harakat, xato emas.

SharedFlow Channel-dan nimasi bilan farq qiladi?

Channel — FIFO navbat, unda har bir hodisa aynan bitta obunachiga yetkaziladi (nuqta-nuqta). SharedFlow — translyatsiya: har bir hodisa BARCHA faol obunachilarga yetkaziladi. SharedFlow BroadcastChannel-ga (deprecated) yaqinroq va „birdan-ko'pga“ stsenariylari uchun mos keladi. Channel — „birdan-birga“ uchun (thread hovuzlari, pipeline). JetBrains tavsiyasiga ko'ra, SharedFlow barcha yangi loyihalar uchun BroadcastChannel o'rnini bosuvchi hisoblanadi.

SharedFlow-ni qanday thread-safe qilish mumkin?

SharedFlow allaqachon thread-safe — emit() va collect() to'g'ri sinxronlashtirilgan. Bir nechta thread-lar blokirovkalarsiz emit() chaqirishi mumkin va barcha faol obunachilar hodisalarni to'g'ri tartibda oladi. tryEmit() bloklamaydigan — bufer to'la bo'lsa false qaytaradi. Yuqori yuklangan tizimlar uchun DROP_OLDEST bilan tryEmit() dan foydalaning — bu thread-larning bloklanishining oldini oladi.

Nega SharedFlow holat uchun ishlatilmaydi?

replay=1 bo'lmagan SharedFlow oxirgi qiymatni saqlamaydi — ekran aylanganda yangi obunachi joriy holatni olmaydi, UI bo'sh qoladi. replay=1 bilan SharedFlow StateFlow kabi harakat qiladi, lekin equals() orqali taqqoslash optimallashtirishini yo'qotadi, bu esa bir xil qiymatni qayta yuborishda keraksiz bildirishnomalarga sabab bo'ladi. StateFlow — holat uchun to'g'ri tanlov; SharedFlow — hodisalar uchun.

SharedFlow-ni qanday test qilish kerak?

SharedFlow-ni test qilish uchun Turbine — Flow-ni test qilish uchun Kotlin kutubxonasidan foydalaning. Turbine har bir emissiyani alohida, taym-autlar va tugash tekshiruvi bilan tekshirish imkonini beradi. Misol: viewModel.event.test { assertEquals(UiEvent.ShowSnackbar("OK"), awaitItem()) }. Shuningdek, runTest da kutilgan hodisalar sonini ko'rsatib .toList() dan foydalanish mumkin.

Xulosa

  • SharedFlow — oxirgi holatga bog'liq bo'lmagan bir martalik hodisalar uchun issiq reaktiv oqim.
  • SharedFlow vs StateFlow: SharedFlow — hodisalar (navigatsiya, toastlar, ogohlantirishlar), StateFlow — holat (ro'yxatlar, yuklash, xatolar).
  • MutableSharedFlow replay=0, extraBufferCapacity=5, DROP_OLDEST — UI hodisalari uchun standart konfiguratsiya.
  • emit() — bloklovchi yuborish uchun suspend-funksiya; tryEmit() — Boolean natija bilan non-suspend.
  • Sealed class bilan UiEvent namunasi — ViewModel-dan View-ga bir martalik hodisalarni uzatish uchun tavsiya etilgan usul.
  • SharedFlow har bir obunachiga yetkazishni kafolatlaydi, lekin bufer to'lib toshganda har bir hodisaning yetkazilishini kafolatlamaydi.
  • Bitta ViewModel da SharedFlow + StateFlow kombinatsiyasi — holat va hodisalarni ajratuvchi arxitektura uchun optimal namunadir.

Biz kalit topshirig'i bilan mobil ilovani ishlab chiqamiz

IT Sectr 2017-yildan beri startaplar va korxonalar uchun iOS va Android ilovalarini yaratadi. Biz sizga maslahat beramiz va eng yaxshi yechimni taklif qilamiz.

Loyihani muhokama qilish

Shuningdek o'qing