SharedFlow: nədir, SharedFlow vs StateFlow Android-də

Müəllif: IT Sectr Dərc olunub: 2026-02-20 Oxuma vaxtı: 9 dəq

SharedFlow — Kotlin Coroutines kitabxanasından isti reaktiv axın, ekranın çevrilməsi və ya abunəçinin yenidən yaradılması zamanı təkrarlanmaması lazım olan birdəfəlik hadisələr (one-shot events) üçün optimallaşdırılıb. SharedFlow-un StateFlow-dan nə ilə fərqləndiyini göstəririk: StateFlow-dan fərqli olaraq, SharedFlow yeni abunəçilər üçün son dəyəri saxlamır və replay, extraBufferCapacity və onBufferOverflow konfiqurasiyasını dəstəkləyir. Google (Android Developers, 2025) məlumatına görə, SharedFlow naviqasiya əmrləri, Snackbar mesajları və dəqiq bir dəfə işlənməli olan digər hadisələr üçün tövsiyə olunan həll yoludur.

Əsas

  • SharedFlow — birdəfəlik hadisələr üçün hot flow: yeni abunəçilər replay qurulmadan əvvəlki dəyərləri almır.
  • MutableSharedFlow — hadisələri göndərmək üçün emit() və tryEmit() metodları ilə dəyişdirilə bilən versiya.
  • SharedFlow vs StateFlow: SharedFlow dəyərləri konflyasiya etmir (bir neçəsini buferləşdirə bilər), ilkin dəyər tələb etmir, birdəfəlik hadisələr üçün uyğundur.
  • replay — yeni abunəçilərə oxudulan son hadisələrin sayı (standart 0).
  • extraBufferCapacity — replay-dan əlavə hadisələr üçün bufer, emit()-in bloklanmasının qarşısını alır.

Kotlin-də SharedFlow nədir?

SharedFlow — kotlinx.coroutines.flow kitabxanasından isti axın (hot flow) olub, StateFlow-dan fərqli olaraq, bir vəziyyətə bağlı deyil və ixtiyari sayda hadisəni ixtiyari abunəçilərə emit edə bilər. SharedFlow StateFlow üçün əsas tipdir — StateFlow replay = 1 ilə SharedFlow vasitəsilə reallaşdırılıb.

SharedFlow-un əsas xüsusiyyəti — son dəyəri saxlamaq məcburiyyətində deyil. Standart olaraq (replay = 0) yeni abunəçi yeni hadisə göndərilənə qədər heç nə almır. Bu, SharedFlow-u hadisənin dəqiq bir dəfə işlənməli olduğu ssenarilər üçün ideal edir: naviqasiya, Snackbar, sistem bildirişləri, QR kod skan nəticələri.

SharedFlow kotlinx.coroutines 1.4.0 (noyabr 2020) ilə birlikdə StateFlow ilə stabilləşdirilib. Kotlin Coroutines (2025) sənədlərinə görə, SharedFlow abunəçilərin sinxronizasiyası üçün incə dənəli bloklamadan istifadə edir və JetBrains testləri ilə təsdiqlənmiş performans deqradasiyası olmadan 1000+ eyni vaxtda abunəçiyə qədər xətti miqyaslana bilirlik təmin edir.

SharedFlow vs StateFlow: nə vaxt nə istifadə etməli

SharedFlow və StateFlow arasında seçim ötürülən məlumatların semantikasından asılıdır: vəziyyət (StateFlow) və ya hadisə (SharedFlow). Aşağıda nümunələrlə aydın meyarlar verilmişdir.

MeyarSharedFlowStateFlow
SemantikaBirdəfəlik hadisələr (naviqasiya, toast, alert)UI vəziyyəti (siyahı, yükləmə, xəta)
İlkin dəyərTələb olunmurMəcburidir
Abunəlikdə təkrarlamaYalnız replay > 0 olduqdaHəmişə son dəyər
KonflyasiyaXeyr — hadisələr itmir (bufer dolmasa)Bəli — yalnız sonuncunu saxlayır
Buferləşdirməreplay + extraBufferCapacity ilə konfiqurasiya olunurYalnız 1 (replay=1 sabit)
İstifadənavigationEvent, showSnackbar, openDialogitems, isLoading, uiState

Ən sadə qayda: əgər məlumatlar ekran çevrildikdə göstərilməlidirsə — bu vəziyyətdir (StateFlow). Ekran çevrildikdə hadisə təkrarlanmamalıdırsa — bu birdəfəlik hadisədir (SharedFlow). Məsələn, „xəta mesajı ilə toast“ — SharedFlow: çevrilmə zamanı toast yenidən göstərilməməlidir. „Məhsul siyahısı“ — StateFlow: çevrilmə zamanı siyahı ekranda qalmalıdır.

IT Sectr-də SharedFlow-u istifadə edirik: naviqasiya əmrləri (ekrana keçid, dərin link açma), UI hadisələri (Snackbar, AlertDialog), sistem bildirişləri (fon məlumatlarının yenilənməsi, ödəniş nəticəsi), analitik hadisələr (loglama, izləmə).

MutableSharedFlow: emit, tryEmit və buferləşdirmə

MutableSharedFlow — hadisələri göndərmək üçün emit() (suspend) və tryEmit() (non-suspend) metodları ilə SharedFlow-un dəyişdirilə bilən versiyası. emit() bufer doludursa və onBufferOverflow = SUSPEND olduqda dayanır. tryEmit() Boolean qaytarır — hadisənin buferə uğurla əlavə edilib-edilmədiyini.

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 parametrləri kritik əhəmiyyətlidir: replay = 0 hadisənin yeni abunəçi üçün təkrarlanmayacağını təmin edir; extraBufferCapacity = 10 — UI abunə olana qədər sürətli hadisə göndərmə halı üçün bufer; DROP_OLDEST — daşma strategiyası: köhnə hadisələr atılır, yeniləri saxlanılır. Kotlin Coroutines Performance (JetBrains, 2024) məlumatına görə, extraBufferCapacity = 64 olan SharedFlow itkisiz olaraq saniyədə 100 000-dən çox hadisə emal edir.

Birdəfəlik hadisələr üçün SharedFlow: Event nümunəsi

Event (və ya UiEvent) nümunəsi — Google tərəfindən ViewModel-dən View-ə birdəfəlik hadisələrin ötürülməsi üçün tövsiyə olunan üsuldur. Vəziyyətdən (StateFlow) fərqli olaraq, hadisə dəqiq bir dəfə işlənməli və ekran çevrildikdə təkrarlanmamalıdır. replay = 0 olan SharedFlow bu tapşırıq üçün idealdır.

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 ?: "Formatlaşdırma xətası"))
            }
        }
    }
}

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

View-də (Activity/Fragment): event-ə abunəlik lifecycleScope-də repeatOnLifecycle(STATE.STARTED) ilə yerinə yetirilməlidir. STARTED-ə hər daxil olmada abunəlik yenidən yaradılır, lakin hadisə təkrarlanmır, çünki replay=0 olan SharedFlow onu artıq boşaltmışdır. Bu, sifariş izləmə ekranına naviqasiyanın hər çevrilmədə deyil, yalnız bir dəfə baş verməsini təmin edir.

SharedFlow parametrləri: replay, extraBufferCapacity, onBufferOverflow

MutableSharedFlow konstruktoru buferin davranışını təyin edən üç parametr qəbul edir. Yanlış konfiqurasiya hadisələrin itməsinə və ya emit()-in bloklanmasına səbəb ola bilər.

ParametrTipStandartTəsvir
replayInt0Yeni abunəçiyə oxudulan son hadisələrin sayı. 0 = oxutma, 1 = StateFlow kimi
extraBufferCapacityInt0Replay-dan əlavə bufer. Hadisələr dairəvi buferdə saxlanılır. 64 — əksər ssenarilər üçün tövsiyə olunan limit
onBufferOverflowBufferOverflowSUSPENDBufer dolduqda strategiya: SUSPEND, DROP_OLDEST, DROP_LATEST
kotlin
// Müxtəlif ssenarilər üçün konfiqurasiyalar:

// 1. Birdəfəlik UI hadisələri (naviqasiya, toastlar)
val uiEvents = MutableSharedFlow<UiEvent>(
    replay = 0,
    extraBufferCapacity = 5,
    onBufferOverflow = BufferOverflow.DROP_OLDEST
)

// 2. Vəziyyət sinxronizasiyası üçün replay axını (StateFlow kimi)
val stateLike = MutableSharedFlow<AppState>(
    replay = 1,
    extraBufferCapacity = 0
)

// 3. Yüksək tezlikli hadisə göndərmə (analitika, loglar)
val analytics = MutableSharedFlow<AnalyticsEvent>(
    replay = 0,
    extraBufferCapacity = 100,
    onBufferOverflow = BufferOverflow.DROP_OLDEST
)

Vacib: extraBufferCapacity + replay = ümumi bufer ölçüsü. emit() abunəçinin hadisəni emal etməsindən daha sürətli çağırılırsa, bufer dolur və onBufferOverflow işə düşür. UI hadisələri üçün DROP_OLDEST — təhlükəsiz strategiya: köhnə hadisələr (artıq aktual olmayan naviqasiyalar) yeniləri lehinə atılır. Maliyyə əməliyyatları üçün SUSPEND istifadə edin — bu, göndərənin bloklanması hesabına heç bir hadisənin itirilməməsinə zəmanət verir.

Kod nümunələri: Kotlin-də SharedFlow

Nümunə 1: Jetpack Navigation ilə SharedFlow

Naviqasiya əmrləri — SharedFlow üçün klassik istifadə halıdır. Fragment hadisələrə abunə olur və naviqasiyanı yerinə yetirir. Ekran çevrildikdə əmr təkrarlanmır.

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-də:
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()
            }
        }
    }
}

Nümunə 2: Room və Flow operatorları ilə SharedFlow

Mürəkkəb ssenari: UI üçün StateFlow ilə birləşdirərək fon hadisələri haqqında bildirişlər üçün 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("Yeni bildiriş: ${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("Bütün bildirişlər oxunmuş kimi qeyd edildi")
        }
    }
}

Bu nümunədə: StateFlow bildiriş siyahısını saxlayır (vəziyyət — çevrilmədə qorunur), SharedFlow toast mesajlarını emit edir (birdəfəlik hadisələr — çevrilmədə təkrarlanmır). İki Flow tipinin birləşməsi — 2022-ci ildən Google tərəfindən ViewModel üçün tövsiyə olunan nümunədir.

Tez-tez verilən suallar

SharedFlow hadisəni itirə bilərmi?

Bəli, əgər bufer doludursa və onBufferOverflow = DROP_OLDEST və ya DROP_LATEST olarsa. SharedFlow hər hadisənin çatdırılmasına zəmanət vermir — bu mesaj növbəsi deyil (Channel kimi). Bütün hadisələrin zəmanətli çatdırılmasına ehtiyacınız varsa, daşmayan buferlə (UNLIMITED) Channel və ya BroadcastChannel (deprecated) istifadə edin. UI hadisələri üçün köhnəlmiş hadisələrin itirilməsi (məsələn, köhnə naviqasiya) — gözlənilən davranışdır, səhv deyil.

SharedFlow Channel-dan nə ilə fərqlənir?

Channel — FIFO növbəsi, burada hər hadisə dəqiq bir abunəçiyə çatdırılır (nöqtə-nöqtə). SharedFlow — yayım: hər hadisə BÜTÜN aktiv abunəçilərə çatdırılır. SharedFlow BroadcastChannel-ə (deprecated) daha yaxındır və „birdən-çoxa“ ssenariləri üçün uyğundur. Channel — „birdən-birə“ üçün (thread hovuzları, pipeline). JetBrains tövsiyəsinə görə, SharedFlow bütün yeni layihələr üçün BroadcastChannel-in əvəzidir.

SharedFlow-u thread-safe necə etməli?

SharedFlow artıq thread-safe-dir — emit() və collect() düzgün sinxronizasiya olunub. Çoxlu thread-lar bloklamalar olmadan emit() çağıra bilər və bütün aktiv abunəçilər hadisələri düzgün ardıcıllıqla alır. tryEmit() bloklamayan — bufer doludursa false qaytarır. Yüksək yüklü sistemlər üçün DROP_OLDEST ilə tryEmit() istifadə edin — bu, thread-ların bloklanmasının qarşısını alır.

Niyə SharedFlow vəziyyət üçün istifadə edilmir?

replay=1 olmadan SharedFlow son dəyəri saxlamır — ekran çevrildikdə yeni abunəçi cari vəziyyəti almır, UI boş qalır. replay=1 ilə SharedFlow StateFlow kimi davranır, lakin equals() vasitəsilə müqayisə optimallaşdırmasını itirir, bu da eyni dəyərin təkrar göndərilməsi zamanı lazımsız bildirişlərə səbəb olur. StateFlow — vəziyyət üçün düzgün seçimdir; SharedFlow — hadisələr üçün.

SharedFlow-u necə test etməli?

SharedFlow-u test etmək üçün Turbine — Flow-u test etmək üçün Kotlin kitabxanasından istifadə edin. Turbine hər emissiyanı ayrıca, timeoutlar və tamamlama yoxlaması ilə yoxlamağa imkan verir. Nümunə: viewModel.event.test { assertEquals(UiEvent.ShowSnackbar("OK"), awaitItem()) }. Həmçinin runTest-də gözlənilən hadisələrin sayını göstərərək .toList() istifadə etmək olar.

Nəticə

  • SharedFlow — son vəziyyətə bağlı olmayan birdəfəlik hadisələr üçün isti reaktiv axın.
  • SharedFlow vs StateFlow: SharedFlow — hadisələr (naviqasiya, toastlar, alertlər), StateFlow — vəziyyət (siyahılar, yükləmə, xətalar).
  • MutableSharedFlow replay=0, extraBufferCapacity=5, DROP_OLDEST — UI hadisələri üçün standart konfiqurasiya.
  • emit() — bloklayıcı göndərmə üçün suspend-funksiya; tryEmit() — Boolean nəticə ilə non-suspend.
  • Sealed class ilə UiEvent nümunəsi — ViewModel-dən View-ə birdəfəlik hadisələrin ötürülməsi üçün tövsiyə olunan üsul.
  • SharedFlow hər abunəçiyə çatdırılmanı təmin edir, lakin bufer daşması zamanı hər hadisənin çatdırılmasına zəmanət vermir.
  • Bir ViewModel-də SharedFlow + StateFlow birləşməsi — vəziyyəti və hadisələri ayıran arxitektura üçün optimal nümunədir.

Açar təslim mobil tətbiq hazırlayacağıq

IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.

Layihəni müzakirə et

Həm də oxuyun