StateFlow
ו-SharedFlow
הם Flow APIs
שמאפשרת לתהליכי פליטה אופטימלית של עדכוני מצב ופליטת ערכים
צרכנים.
StateFlow
StateFlow
הוא תהליך גלוי לכולם שפולט את המצב הנוכחי והחדש
ועדכונים לאספנים. אפשר גם לקרוא את ערך המצב הנוכחי עד
value
לנכס. כדי לעדכן את המצב ולשלוח אותו לזרימה, יש להקצות ערך חדש ל-
המאפיין value
של
כיתה MutableStateFlow
.
ב-Android, האפליקציה StateFlow
מתאימה מאוד לכיתות שצריך לתחזק
מצב ניתן למדידה.
בהמשך לדוגמאות של תהליכים של קוטלין, 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
הם הצרכנים. ביטול הלייק
תהליך קר שנוצר באמצעות ה-builder של flow
, StateFlow
הוא חם:
האיסוף מהתהליך לא מפעיל קוד של היצרן. StateFlow
תמיד פעיל ונמצא בזיכרון, והוא הופך למתאים לאשפה
רק כשאין התייחסות אחרת לאשפה
הרמה הבסיסית (root) של האוסף.
כשצרכן חדש מתחיל לאסוף מהתהליך, הוא מקבל את
ועל המצבים הבאים: אפשר לראות את ההתנהגות הזו
בסיווגים אחרים שניתנים למדידה,
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
מחייב העברה של מצב ראשוני ל-constructor, ואילוLiveData
לא.LiveData.observe()
מבטל את רישום הצרכן באופן אוטומטי כאשר מגיע למצבSTOPPED
, ואילו איסוף מ-StateFlow
או כל תהליך אחר לא מפסיק לאסוף באופן אוטומטי. כדי להשיג צריך לאסוף את הזרימה מ-Lifecycle.repeatOnLifecycle
חסימה.
הגברת זרימת הקור באמצעות shareIn
StateFlow
הוא זרם חם – הוא נשאר בזיכרון כל עוד
איסוף אשפה או כל אזכור אחר שלו מתוך אוסף אשפה
בסיס. אפשר להפוך זרם קר לחום באמצעות
shareIn
.
שימוש ב-callbackFlow
שנוצר בתהליכים של קוטלין בתור
לדוגמה, במקום שכל אספן ייצור תהליך חדש, אפשר לשתף
את הנתונים שאוחזרו מ-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()
מדיניות ההתחלה שומרת על מפיק ה-upstream פעיל כל עוד
מנויים. יש עוד כללי מדיניות להתחלה, כמו
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