StateFlow
وSharedFlow
هما واجهات برمجة تطبيقات للتدفق
تتيح للتدفقات إصدار تحديثات الحالة على نحو مثالي وإصدار قيم لعدة مستهلكين.
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
إذا كنت لا تريد إعادة تشغيل أحدث المعلومات التي تم إرسالها إلى التدفق.
موارد التدفق الإضافية
- تدفق Kotlin على Android
- اختبار تدفقات لغة Kotlin على Android
- معلومات عن عاملَي التشغيل ShareIn وstateIn في التدفق
- نقل البيانات من LiveData إلى تدفق Kotlin
- مراجع إضافية حول كورروتينات Kotlin وتدفقها