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 UI 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 : ComponentActivity() { private val latestNewsViewModel: LatestNewsViewModel = TODO() // 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به آن نیاز ندارد. - وقتی نما به حالت
STOPPEDمیرود،LiveData.observe()بهطور خودکار ثبت مصرفکننده را لغو میکند، درحالیکه جمعآوری ازStateFlowیا هر جریان دیگری بهطور خودکار متوقف نمیشود. برای دستیابی به همان عملکرد، باید جریان را ازLifecycle.repeatOnLifecycleبلوک جمعآوری کنید.
درحال داغ کردن جریانهای سرد بااستفاده از shareIn
جریان StateFlow یک جریان داغ است—تا زمانی که جریان جمعآوری شود یا تا زمانی که هرگونه مرجع دیگری به آن از ریشه جمعآوری زباله وجود داشته باشد، در حافظه باقی میماند. بااستفاده از
shareIn
اپراتور میتوانید جریانهای سرد را داغ کنید.
بااستفاده از callbackFlow ایجادشده در جریانهای Kotlin بهعنوان مثال، بهجای اینکه هر جمعآوریکننده جریان جدیدی ایجاد کند، میتوانید بااستفاده از shareIn دادههای بازیابیشده از Firestore را بین جمعآوریکنندهها همرسانی کنید.
باید موارد زیر را ارسال کنید:
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<Unit> = _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
- جریانها