StateFlow
وSharedFlow
هما واجهتا برمجة تطبيقات Flow
تتيحان للعمليات إرسال تعديلات الحالة بشكلٍ مثالي وبث القيم إلى عدة
مستخدِمين.
StateFlow
StateFlow
هو تدفق يمكن ملاحظته من قِبل مالك الحالة والذي يُصدر تعديلات الحالة الحالية والجديدة على جامعي السلع. يمكن أيضًا قراءة قيمة الحالة الحالية من خلال سمة
value
. لتعديل الحالة وإرسالها إلى المسار، عليك تخصيص قيمة جديدة للسمة value
للفئة MutableStateFlow
.
في Android، يكون StateFlow
مناسبًا تمامًا للفئات التي تحتاج إلى الاحتفاظ
بحالة قابلة للتغيير يمكن رصدها.
باتّباع الأمثلة الواردة في عمليات Kotlin، يمكن عرض StateFlow
من LatestNewsViewModel
حتى تتمكّن View
من
الاستماع إلى تعديلات حالة واجهة المستخدم وجعل حالة الشاشة تبقى intact
عند تغيير الإعدادات.
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 كأحد مثالي، بدلاً من أن ينشئ كلّ مجمّع عملية جديدة، يمكنك مشاركة data التي تم استرجاعها من 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
إذا كنت لا تريد إعادة تشغيل آخر المعلومات المُرسَلة إلى العملية.
موارد إضافية حول مسار الإحالة الناجحة
- مسارات Kotlin على Android
- اختبار مسارات Kotlin على Android
- معلومات يجب معرفتها عن عاملَي تشغيل shareIn وstateIn في Flow
- نقل البيانات من LiveData إلى Kotlin Flow
- مراجع إضافية حول الكوروتينات في لغة Kotlin وتدفق البيانات