StateFlow وSharedFlow

StateFlow وSharedFlow هما واجهات برمجة تطبيقات Flow التي تمكّن التدفقات من إصدار تحديثات الحالة على النحو الأمثل وإطلاق قيم متعددة المستهلكين.

StateFlow

StateFlow هو تدفق يمكن ملاحظته من قِبل صاحب الحالة ينبعث من الحالة الحالية والجديدة تحديثات للمجمعين. ويمكن أيضًا قراءة قيمة الحالة الحالية من خلال value الموقع. لتعديل الحالة وإرسالها إلى التدفق، عيِّن قيمة جديدة إلى السمة value MutableStateFlow.

في 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 وFlow وLiveData

StateFlow وLiveData لديهما وأوجه التشابه. كلاهما فئتان لصاحبي بيانات يمكن ملاحظتهما، وكلاهما يتبع بنمط مشابه عند استخدامه في بنية تطبيقك.

ومع ذلك، تجدر الإشارة إلى أنّ StateFlow يختلف سلوك استخدام LiveData:

  • تتطلب الدالة StateFlow تمرير حالة أولية إلى الدالة الإنشائية، بينما لا يستخدمها LiveData.
  • يلغي LiveData.observe() تسجيل المستهلك تلقائيًا عند ينتقل إلى حالة STOPPED، بينما يتم جمعه من StateFlow أو ولن يتوقف أي تدفق آخر عن التجميع تلقائيًا. لتحقيق نفس عليك جمع التدفق من Lifecycle.repeatOnLifecycle حظر.

تشغيل تدفقات باردة ساخنة باستخدام shareIn

StateFlow هو مسار ساخن، ويبقى في الذاكرة طالما كان التدفق البيانات المجمّعة أو عندما توجد أي إشارات أخرى إليها من مجموعة البيانات غير المرغوب فيها جذر. يمكنك زيادة درجة حرارة التدفق البارد باستخدام shareIn .

استخدام callbackFlow التي تم إنشاؤها في تدفقات Kotlin سبيل المثال، فبدلاً من أن ينشئ كل جامع تدفقًا جديدًا، يمكنك مشاركة البيانات المأخوذة من Firestore بين التجميعات باستخدام shareIn. يجب اجتياز ما يلي:

  • تمثّل هذه السمة 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 لبدء المشاركة بعد ظهور أول مشترك والحفاظ على التدفق نشطًا إلى الأبد.

حدث SharedFlow

تعرض الدالة shareIn المسار SharedFlow، وهو تدفق ساخن يُصدر قيمًا. لجميع المستهلكين الذين يجمعون منها. SharedFlow هو تعميم StateFlow قابل للضبط بشكل كبير.

يمكنك إنشاء "SharedFlow" بدون استخدام "shareIn". على سبيل المثال، يمكن استخدام 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 إذا كنت لا ترغب في إعادة تشغيل آخر المعلومات المرسلة إلى التدفق.

موارد التدفق الإضافية