סקירה כללית של ViewModel חלק מ-Android Jetpack.
המחלקה ViewModel
היא לוגיקה עסקית או מצב ברמת המסך
holder. הוא חושף את המצב לממשק המשתמש ומקיף את הלוגיקה העסקית הקשורה.
היתרון העיקרי שלו הוא שהמצב נשמר במטמון ונשמר בו
שינויים בתצורה. המשמעות היא שממשק המשתמש לא צריך לאחזר שוב נתונים
במהלך ניווט בין פעילויות או בעקבות שינויים בהגדרות, כמו
בעת סיבוב המסך.
למידע נוסף על בעלי מדינה, אפשר לעיין בהנחיות בנושא בעלי מדינה. באופן דומה, למידע נוסף על שכבת ממשק המשתמש באופן כללי, ראו שכבת ממשק המשתמש. הנחיה.
היתרונות של ViewModel
החלופה ל-ViewModel היא מחלקה פשוטה שמכילה את הנתונים שאתם מציגים בממשק המשתמש. הדבר עלול להוות בעיה כאשר עוברים בין פעילויות או יעדי ניווט. הפעולה הזו תשמיד את הנתונים האלה אם לא תאחסנו אותם. באמצעות מנגנון שמירת מצב המכונה. באמצעות ViewModel API לשמירת נתונים שפותר את הבעיה.
היתרונות העיקריים של המחלקה ViewModel הם בעיקרון:
- הוא מאפשר לשמור על מצב ממשק המשתמש.
- הוא מספק גישה ללוגיקה עסקית.
התמדה
התכונה ViewModel מאפשרת שמירה על עקביות גם במצב של ViewModel וגם הפעולות ש-ViewModel מפעיל. השמירה במטמון פירושה שלא כדי לאחזר שוב נתונים באמצעות שינויים נפוצים בהגדרות, כמו בסבב.
היקף
כשאתם יוצרים מופע של ViewModel, מעבירים לו אובייקט שמיישם את
ViewModelStoreOwner
. זה יכול להיות יעד ניווט,
תרשים ניווט, פעילות, מקטע, או כל סוג אחר שמטמיע את
גרפי. לאחר מכן המערכת מכסה את ה-ViewModel שלכם על מחזור החיים של
ViewModelStoreOwner
. הפריט נשאר בזיכרון עד ViewModelStoreOwner
נעלם באופן סופי.
טווח של סיווגים הוא מחלקה ישירה או עקיפה של
ממשק ViewModelStoreOwner
. מחלקות המשנה הישירות הן
ComponentActivity
, Fragment
ו-NavBackStackEntry
.
לרשימה מלאה של מחלקות משנה עקיפות אפשר לעיין
קובץ עזר של ViewModelStoreOwner
.
לאחר השמדת המקטע או הפעילות שאליהם ה-ViewModel מסתיים, העבודה האסינכרונית ממשיכה ב-ViewModel בהיקף שלה. כאן של הקבוע.
מידע נוסף זמין בקטע שבהמשך בנושא מחזור החיים של ViewModel.
הכינוי של SaveState
SavedStateHandle מאפשר לשמור נתונים לא רק באמצעות הגדרה לשינויים, אלא תוך כדי חשיבה מחדש על תהליך היצירה. כלומר, היא מאפשרת לכם לשמור על במצב תקין גם אם המשתמש סוגר את האפליקציה ופותח אותה במועד מאוחר יותר.
גישה ללוגיקה עסקית
למרות שהרוב המכריע של הלוגיקה העסקית נמצא בנתונים שכבת ה-UI, גם יכולה להכיל לוגיקה עסקית. מצב כזה יכול להיות כאשר לשלב נתונים מכמה מאגרים כדי ליצור את מצב ממשק המשתמש של המסך, סוג מסוים של נתונים לא מחייב שכבת נתונים.
ViewModel הוא המקום הנכון לטיפול בלוגיקה עסקית בשכבת ממשק המשתמש. בנוסף, ViewModel אחראי על הטיפול באירועים ומאציל אותם לגורמים אחרים בהיררכיה כאשר צריך להחיל לוגיקה עסקית כדי לשנות של נתוני האפליקציה.
Jetpack פיתוח נייטיב
כשמשתמשים ב-Jetpack פיתוח נייטיב, ViewModel הוא האמצעי העיקרי לחשיפת ממשק המשתמש של המסך של התכנים הקומפוזביליים. באפליקציה היברידית, פעילויות ומקטעים פשוט מארחים את הפונקציות הקומפוזביליות. זהו שינוי מגישות קודמות, שבהן זה לא היה מכיוון שהוא פשוט ואינטואיטיבי כדי ליצור חלקים לשימוש חוזר בממשק משתמש עם פעילויות ולכן הם היו פעילים הרבה יותר בתור בקרי ממשק משתמש.
הדבר שהכי חשוב לזכור כשמשתמשים ב-ViewModel עם 'כתיבה' הוא
שאי אפשר להגדיר היקף של ViewModel לתוכן קומפוזבילי. זה בגלל שמודל קומפוזבילי
אינו ViewModelStoreOwner
. שני מופעים של אותו תוכן קומפוזבילי
יצירה מוזיקלית, או שתי תכנים קומפוזביליים שונים שיש להם גישה לאותו סוג ViewModel
תחת אותו ViewModelStoreOwner
יקבל את אותו מופע של
ל-ViewModel, ולרוב זו לא ההתנהגות הצפויה.
כדי ליהנות מהיתרונות של ViewModel ב-Compose, צריך לארח כל מסך ב-Fragment או פעילות, או להשתמש בניווט בכתיבה ולהשתמש ב-ViewModels בתוכן קומפוזבילי פועל קרוב ככל האפשר ליעד הניווט. הסיבה לכך היא אפשר להגדיר את ההיקף מ-ViewModel ליעדי ניווט, לתרשימי ניווט, פעילויות ומקטעים.
מידע נוסף מופיע במדריך בנושא העלאת מצב של גודל עבור Jetpack Compose.
הטמעת ViewModel
הדוגמה הבאה היא הטמעה של ViewModel במסך שמאפשר למשתמש להטיל קוביות.
Kotlin
data class DiceUiState(
val firstDieValue: Int? = null,
val secondDieValue: Int? = null,
val numberOfRolls: Int = 0,
)
class DiceRollViewModel : ViewModel() {
// Expose screen UI state
private val _uiState = MutableStateFlow(DiceUiState())
val uiState: StateFlow<DiceUiState> = _uiState.asStateFlow()
// Handle business logic
fun rollDice() {
_uiState.update { currentState ->
currentState.copy(
firstDieValue = Random.nextInt(from = 1, until = 7),
secondDieValue = Random.nextInt(from = 1, until = 7),
numberOfRolls = currentState.numberOfRolls + 1,
)
}
}
}
Java
public class DiceUiState {
private final Integer firstDieValue;
private final Integer secondDieValue;
private final int numberOfRolls;
// ...
}
public class DiceRollViewModel extends ViewModel {
private final MutableLiveData<DiceUiState> uiState =
new MutableLiveData(new DiceUiState(null, null, 0));
public LiveData<DiceUiState> getUiState() {
return uiState;
}
public void rollDice() {
Random random = new Random();
uiState.setValue(
new DiceUiState(
random.nextInt(7) + 1,
random.nextInt(7) + 1,
uiState.getValue().getNumberOfRolls() + 1
)
);
}
}
לאחר מכן אפשר לגשת ל-ViewModel מפעילות באופן הבא:
Kotlin
import androidx.activity.viewModels
class DiceRollActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// Create a ViewModel the first time the system calls an activity's onCreate() method.
// Re-created activities receive the same DiceRollViewModel instance created by the first activity.
// Use the 'by viewModels()' Kotlin property delegate
// from the activity-ktx artifact
val viewModel: DiceRollViewModel by viewModels()
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect {
// Update UI elements
}
}
}
}
}
Java
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Create a ViewModel the first time the system calls an activity's onCreate() method.
// Re-created activities receive the same MyViewModel instance created by the first activity.
DiceRollViewModel model = new ViewModelProvider(this).get(DiceRollViewModel.class);
model.getUiState().observe(this, uiState -> {
// update UI
});
}
}
Jetpack פיתוח נייטיב
import androidx.lifecycle.viewmodel.compose.viewModel
// Use the 'viewModel()' function from the lifecycle-viewmodel-compose artifact
@Composable
fun DiceRollScreen(
viewModel: DiceRollViewModel = viewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
// Update UI elements
}
שימוש בקורוטינים ב-ViewModel
ViewModel
כולל תמיכה בקורוטינים ב-Kotlin. הוא יכול להמשיך
אסינכרונית באותו אופן שבו היא נשארת במצב של ממשק המשתמש.
למידע נוסף, אפשר לעיין במאמר שימוש בקורוטינים של Kotlin עם ארכיטקטורה של Android רכיבים.
מחזור החיים של ViewModel
מחזור החיים של ViewModel
קשור ישירות להיקף שלו. ViewModel
נשאר בזיכרון עד לתאריך ViewModelStoreOwner
שאליו היא כוללת
נעלם. מצב כזה עשוי לקרות בהקשרים הבאים:
- במקרה של פעילות, היא מסתיימת.
- במקרה של שבר, כשהוא מתנתק.
- במקרה של רשומת ניווט, היא תוסר מהמקבץ האחורי.
לכן, ViewModels הוא פתרון נהדר לאחסון נתונים שעדיין נשארים שינויים בתצורה.
איור 1 ממחיש את המצבים השונים של מחזור החיים של פעילות בזמן שהיא עוברת
של סיבוב, ואז הוא מסתיים. באיור מוצג גם משך החיים של
ViewModel
לצד מחזור החיים של הפעילות המשויכת. הספציפי הזה
שממחיש את המצבים של פעילות. אותם מצבים בסיסיים חלים על
את מחזור החיים של מקטע.
בדרך כלל מבקשים ViewModel
בפעם הראשונה שהמערכת קוראת
ל-method onCreate()
של אובייקט הפעילות. המערכת עשויה להתקשר
onCreate()
כמה פעמים במהלך עצם קיומה של פעילות, כמו
כמו כשמסובבים את מסך המכשיר. ViewModel
קיים החל מתאריך
לבקש ViewModel
עד שהפעילות תסתיים ותושמד.
ניקוי יחסי התלות של ViewModel
ה-ViewModel קורא לשיטה onCleared
כאשר ViewModelStoreOwner
משמידים אותו במהלך מחזור החיים שלו. כך אפשר לנקות כל מקום עבודה
או יחסי תלות שתואמים למחזור החיים של ViewModel.
הדוגמה הבאה מציגה חלופה ל-viewModelScope
.
viewModelScope
הוא CoroutineScope
מובנה
עוקב באופן אוטומטי אחרי מחזור החיים של ViewModel. ה-ViewModel משתמש בו כדי
להפעיל פעולות עסקיות. אם רוצים להשתמש במקום זאת בהיקף מותאם אישית
של viewModelScope
לבדיקה קלה יותר, ה-ViewModel יכול לקבל
CoroutineScope
כתלות ב-constructor שלה. כאשר
ViewModelStoreOwner
מנקה את ה-ViewModel בסוף מחזור החיים שלו,
ViewModel גם מבטל את CoroutineScope
.
class MyViewModel(
private val coroutineScope: CoroutineScope =
CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
) : ViewModel() {
// Other ViewModel logic ...
override fun onCleared() {
coroutineScope.cancel()
}
}
מגרסה 2.5 ואילך במחזור החיים, אפשר להעביר Closeable
אחת או יותר
אובייקטים ל-constructor של ViewModel שנסגרים אוטומטית,
המופע של ViewModel הוסר.
class CloseableCoroutineScope(
context: CoroutineContext = SupervisorJob() + Dispatchers.Main.immediate
) : Closeable, CoroutineScope {
override val coroutineContext: CoroutineContext = context
override fun close() {
coroutineContext.cancel()
}
}
class MyViewModel(
private val coroutineScope: CoroutineScope = CloseableCoroutineScope()
) : ViewModel(coroutineScope) {
// Other ViewModel logic ...
}
שיטות מומלצות
בהמשך מפורטות כמה שיטות מומלצות עיקריות שכדאי לפעול לפיהן במהלך ההטמעה ViewModel:
- בגלל ההיקפים שלהם, השתמשו ב-ViewModels בתור פרטי ההטמעה של שומר מצב ברמת המסך. אין להשתמש בהם כבעלי מצב של ממשק משתמש לשימוש חוזר רכיבים כמו קבוצות צ'יפים או טפסים. אחרת, מקבלים במופע של ViewModel נעשה שימושים שונים באותו רכיב של ממשק המשתמש במסגרת ViewModelStoreOwner, אלא אם משתמשים במפתח מודל מפורש של תצוגה לכל צ'יפ.
- פרטי ההטמעה של ממשק המשתמש לא ידועים ל-ViewModel. שמירת השמות השיטות ש-ViewModel API חושף וה-methods של שדות המצב של ממשק המשתמש כלליות ככל האפשר. כך, ה-ViewModel יכול לתת מענה לכל סוג ממשק משתמש: טלפון נייד, מתקפל, טאבלט או אפילו Chromebook
- מאחר שהמודל ימשיך להיות ארוך יותר מ-
ViewModelStoreOwner
, מודלים של ViewModel לא יכולה להכיל הפניות לממשקי API שקשורים למחזור החיים, כמוContext
אוResources
כדי למנוע דליפות זיכרון. - אסור להעביר מודלים של ViewModel למחלקות אחרות, לפונקציות אחרות או לרכיבים אחרים של ממשק המשתמש. מאחר שהפלטפורמה מנהלת אותם, כדאי לשמור אותם בקרבת מקום כן. קרוב לפונקציה הקומפוזבילית ברמת הפעילות, מקטע או מסך. כך רכיבים ברמה נמוכה יותר יכולים לגשת ליותר נתונים ולוגיקה מאשר הדרושים להם.
מידע נוסף
ככל שהנתונים נעשים יותר מורכבים, כדאי ליצור כיתה נפרדת
כדי לטעון את הנתונים. המטרה של ViewModel
היא לבצע אנונימיזציה של הנתונים עבור
בקר בממשק משתמש שמאפשר לנתונים לשרוד שינויים בתצורה. למידע
על טעינה, שמירה וניהול של נתונים בשינויים שונים בתצורה,
מצבים שמורים של ממשק המשתמש.
במדריך לארכיטקטורת אפליקציות של Android הצעה ליצור מחלקה של מאגר כדי לטפל בפונקציות האלה.
מקורות מידע נוספים
כדי לקבל מידע נוסף על הכיתה ViewModel
, אפשר לעיין במאמרים הבאים:
המשאבים.
מסמכים
- שכבת ממשק המשתמש
- אירועים בממשק המשתמש
- בעלי מצב ומצב ממשק המשתמש
- סביבת הייצור ברמת המדינה
- שכבת נתונים
דוגמיות
מומלץ עבורך
- הערה: טקסט הקישור מוצג כאשר JavaScript מושבת
- שימוש בקורוטינים של Kotlin עם רכיבים שמותאמים למחזור החיים
- שמירת מצבים בממשק המשתמש
- טעינה והצגה של נתונים בדפים