שגרות ההמשך של Kotlin מספקות API שמאפשר לכתוב קוד אסינכרוני. בעזרת coroutines של Kotlin, אתם יכולים להגדיר CoroutineScope, שיעזור לכם לנהל את מועדי ההפעלה של ה-coroutines. כל פעולה אסינכרונית פועלת בהיקף מסוים.
רכיבים שמודעים למחזור החיים מספקים תמיכה ברמה גבוהה ב-coroutines להיקפים לוגיים באפליקציה. במסמך הזה מוסבר איך להשתמש ביעילות ב-coroutines עם רכיבים שמודעים למחזור החיים.
הוספת יחסי תלות
היקפי ה-coroutine המובנים שמתוארים בנושא הזה כלולים ב-Lifecycle API. חשוב להוסיף את יחסי התלות המתאימים כשמשתמשים בהיקפי ההרשאות האלה.
- לכלי ViewModel ב-Compose, משתמשים ב-
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version"). - לכלי Lifecycle ב-Compose, משתמשים ב-
implementation("androidx.lifecycle:lifecycle-runtime-compose:$lifecycle_version").
היקפי coroutine שמותאמים למחזור החיים
Compose והספריות של מחזור החיים מספקים את ההיקפים המובנים הבאים שאפשר להשתמש בהם באפליקציה.
ViewModelScope
ViewModelScope מוגדר לכל ViewModel באפליקציה. כל coroutine שמופעל בהיקף הזה מבוטל באופן אוטומטי אם ViewModel נמחק. אפשר להשתמש ב-coroutines כשצריך לבצע פעולה רק אם ה-ViewModel פעיל. לדוגמה, אם אתם מחשבים נתונים מסוימים לפריסה, כדאי להגדיר את היקף העבודה ל-ViewModel, כך שאם ה-ViewModel נמחק, העבודה תבוטל באופן אוטומטי כדי להימנע מצריכת משאבים.
אפשר לגשת ל-CoroutineScope של ViewModel דרך המאפיין viewModelScope של ViewModel, כמו בדוגמה הבאה:
class MyViewModel: ViewModel() {
init {
viewModelScope.launch {
// Coroutine that will be canceled when the ViewModel is cleared.
}
}
}
בתרחישים מתקדמים יותר, אפשר להעביר CoroutineScope מותאם אישית ישירות ל-constructor של ViewModel כדי להחליף את ברירת המחדל של viewModelScope. הגישה הזו מאפשרת יותר שליטה וגמישות, במיוחד במקרים הבאים:
בדיקה: אפשר להוסיף
TestScope, וכך קל יותר לשלוט בזמן ולאמת את ההתנהגות של פונקציות ה-coroutine בבדיקות יחידה.הגדרה בהתאמה אישית: אתם יכולים להגדיר את ההיקף עם
CoroutineDispatcherספציפי (כמוDispatchers.Defaultלחישובים כבדים) אוCoroutineExceptionHandlerבהתאמה אישית לפני שה-ViewModel מתחיל לפעול.
היקפי הרשאות שקשורים לקומפוזיציה
תופעות לוואי כמו אנימציות, קריאות לרשת או טיימרים צריכות להיות מוגבלות למחזור החיים של ה-composable. כך, כשפונקציה קומפוזבילית יוצאת מהמסך (יוצאת מהקומפוזיציה), כל ה-coroutines שפועלות מבוטלות באופן אוטומטי כדי למנוע דליפות זיכרון.
ב-Compose יש את ה-API LaunchedEffect כדי לטפל בהיקף של הקומפוזיציה באופן הצהרתי.
LaunchedEffect יוצר CoroutineScope שמאפשר להריץ פונקציות השהיה. ההיקף קשור למחזור החיים של הקומפוזיציה של הפונקציה הקומפוזבילית, ולא ל-Lifecycle של ה-Activity המארחת.
- כניסה: פונקציית ה-coroutine מתחילה כשהפונקציה הניתנת לקישור נכנסת לקומפוזיציה.
- יציאה: ה-coroutine מבוטל כשהרכיב הקומפוזבילי יוצא מהקומפוזיציה.
- הפעלה מחדש: אם אחד מהמפתחות שמועברים אל
LaunchedEffectמשתנה, ה-coroutine הקיים מבוטל ומופעל אחד חדש.
בדוגמה הבאה אפשר לראות איך משתמשים ב-LaunchedEffect כדי ליצור אנימציה של פעימה. ה-coroutine קשור לנוכחות של הרכיב הקומפוזבילי בקומפוזיציה ומגיב לשינויים בהגדרות האישיות:
// Allow the pulse rate to be configured, so it can be sped up if the user is running // out of time var pulseRateMs by remember { mutableLongStateOf(3000L) } val alpha = remember { Animatable(1f) } LaunchedEffect(pulseRateMs) { // Restart the effect when the pulse rate changes while (isActive) { delay(pulseRateMs) // Pulse the alpha every pulseRateMs to alert the user alpha.animateTo(0f) alpha.animateTo(1f) } }
מידע נוסף על LaunchedEffect זמין במאמר תופעות לוואי בפיתוח נייטיב.
איסוף זרימה שמותאם למחזור החיים
כדי לאסוף תהליכים בצורה בטוחה ב-Jetpack Compose, משתמשים ב-API collectAsStateWithLifecycle. הפונקציה הזו ממירה Flow לאובייקט State של Compose ומנהלת בשבילכם באופן אוטומטי את המינוי למחזור החיים. כברירת מחדל, האיסוף מתחיל כשהסטטוס של מחזור החיים הוא STARTED ומפסיק כשהסטטוס של מחזור החיים הוא STOPPED. כדי לשנות את פעולת ברירת המחדל הזו, מעבירים את הפרמטר minActiveState עם שיטת מחזור החיים הרצויה, כמו Lifecycle.State.RESUMED.
בדוגמה הבאה אפשר לראות איך אוספים StateFlow של ViewModel בפונקציה קומפוזבילית:
@Composable private fun ConversationScreen( conversationViewModel: ConversationViewModel = viewModel() ) { val messages by conversationViewModel.messages.collectAsStateWithLifecycle() ConversationScreen( messages = messages, onSendMessage = { message: Message -> conversationViewModel.sendMessage(message) } ) } @Composable private fun ConversationScreen( messages: List<Message>, onSendMessage: (Message) -> Unit ) { MessagesList(messages, onSendMessage) /* ... */ }
איסוף מקביל של כמה תהליכים
ב-Compose, אפשר לאסוף כמה תהליכים במקביל על ידי הצהרה על כמה משתני מצב. מכיוון ש-collectAsStateWithLifecycle מנהל את ההיקף הבסיסי שלו, האיסוף המקביל מטופל באופן אוטומטי:
@Composable
fun DashboardScreen(viewModel: DashboardViewModel = viewModel()) {
// Both flows are collected safely in parallel and will emit updates when either changes, the composables will recompose
val userData by viewModel.userFlow.collectAsStateWithLifecycle()
val feedData by viewModel.feedFlow.collectAsStateWithLifecycle()
// ...
}
חישוב ערכים באופן אסינכרוני באמצעות Flows
כשצריך לחשב ערכים באופן אסינכרוני, משתמשים ב-StateFlow עם האופרטור stateIn.
קטע הקוד הבא משתמש ב-Flow רגיל שהומר ל-StateFlow. הפרמטר WhileSubscribed(5000) משאיר את המינוי פעיל למשך חמש שניות אחרי שממשק המשתמש נעלם, כדי לטפל בשינויים בהגדרות.
val uiState: StateFlow<Result> = flow {
emit(repository.fetchData())
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = Result.Loading
)
משתמשים ב-collectAsStateWithLifecycle כדי להמיר את הערכים שנאספו ל-Compose
State, כך שממשק המשתמש יוכל להתעדכן באופן ריאקטיבי בכל פעם שהנתונים משתנים.
מידע נוסף על מצב זמין במאמר מצב ו-Jetpack Compose.
מקורות מידע נוספים
צפייה בתוכן
דוגמאות
מומלץ בשבילך
- הערה: טקסט הקישור מוצג כש-JavaScript מושבת
- טיפול במחזורי חיים באמצעות רכיבים שמזהים מחזורי חיים
- טעינה והצגה של נתונים בדפים