StateFlow
و SharedFlow
API های Flow هستند که جریان ها را قادر می سازند تا به روز رسانی های حالت را به طور بهینه منتشر کنند و مقادیر را برای چندین مصرف کننده منتشر کنند.
StateFlow
StateFlow
یک جریان قابل مشاهده توسط دارنده حالت است که به روز رسانی وضعیت فعلی و جدید را به جمع کننده های خود منتشر می کند. مقدار وضعیت فعلی را می توان از طریق ویژگی value
آن نیز خواند. برای بهروزرسانی وضعیت و ارسال آن به جریان، یک مقدار جدید به ویژگی value
کلاس MutableStateFlow
اختصاص دهید.
در اندروید، 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()
به طور خودکار مصرف کننده را لغو ثبت می کند زمانی که view به حالت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 در اندروید جریان دارد
- تست جریان های Kotlin در اندروید
- چیزهایی که باید درباره عملگرهای shareIn و stateIn Flow بدانید
- مهاجرت از LiveData به Kotlin Flow
- منابع اضافی برای کوروتین ها و جریان کاتلین