בדף הזה תלמדו על מחזור החיים של תוכן קומפוזבילי האופן שבו התכונה Compose קובע אם תוכן קומפוזבילי צריך להרכיב מחדש.
סקירה כללית של מחזור החיים
כפי שצוין במסמכים לגבי ניהול מצב, הרכב מתאר את ממשק המשתמש של האפליקציה ומופק על ידי הרצה של תכנים קומפוזביליים. הרכב הוא מבנה עץ של התכנים הקומפוזביליים שמתארים את ממשק המשתמש שלכם.
כש-Jetpack Compose מפעיל את ה-composables בפעם הראשונה, במהלך הרכבת הקוד הראשונית, הוא עוקב אחרי ה-composables שאתם קוראים להם כדי לתאר את ממשק המשתמש ב-Composition. לאחר מכן, כשמצב האפליקציה ישתנה, Jetpack Compose יתזמן יצירה מחדש. הרכבה מחדש היא כש-Jetpack פיתוח נייטיב מפעיל מחדש את התכנים הקומפוזביליים שייתכן שהשתנו בתגובה לשינויים במצב, ולאחר מכן מעדכנת את היצירה כך שתשקף את השינויים, אם ישנם.
אפשר ליצור קומפוזיציה רק באמצעות קומפוזיציה ראשונית, ולעדכן אותה באמצעות קומפוזיציה מחדש. הדרך היחידה לשנות יצירה מוזיקלית היא באמצעות הרכבה מחדש.
איור 1. מחזור החיים של רכיב ה-Composable ב-Composition. הוא מזין את יצירה מוזיקלית, הרכבה מחדש 0 פעמים או יותר, ויוצאת מהיצירה.
הרכבה מחדש בדרך כלל מופעלת על ידי שינוי
אובייקט State<T>
. אימייל חדש
שעוקבת אחריה ומריצה את כל התכנים הקומפוזביליים במערך היצירה שקוראת
State<T>
מסוים, וכל תוכן קומפוזבילי שהוא קורא לתוכן שלא יכול
דילגת.
אם קוראים ל-composable כמה פעמים, המערכת תוסיף כמה מכונות ל-Composition. לכל קריאה יש מחזור חיים משלה ב-Composition.
@Composable fun MyComposable() { Column { Text("Hello") Text("World") } }
איור 2. ייצוג של MyComposable
ביצירה. אם
נקרא 'קומפוזביליות' כמה פעמים,
יצירה מוזיקלית. שרכיב בצבע אחר מצביע על כך שהוא
למופע נפרד.
האנטומיה של חומר קומפוזבילי ביצירה
המופע של תוכן קומפוזבילי בקטע 'יצירה' מזוהה על ידי אתר הקריאה שלו. המהדר של Compose מתייחס לכל אתר קריאה כאתר נפרד. קריאה לרכיבים מורכבים ממספר מוקדי קריאה תיצור כמה מופעים של הרכיב המורכב ב-Composition.
אם במהלך יצירת קומפוזיציה מחדש, רכיב מורכב קורא לרכיבים מורכבים שונים מאלה שקרא להם במהלך היצירה הקודמת, Compose יזהה אילו רכיבים מורכבים הוזמנו או לא הוזמנו. לגבי הרכיבים המורכבים שהוזמנו בשתי הקומפוזיציות, Compose ימנע יצירת קומפוזיציה מחדש שלהם אם הקלט שלהם לא השתנה.
שימור הזהות הוא חיוני כדי לשייך תופעות לוואי לתכנים הקומפוזביליים שלהם, כדי שיוכלו להשלים בהצלחה במקום להפעיל אותן מחדש בכל חיבור מחדש.
דוגמה:
@Composable fun LoginScreen(showError: Boolean) { if (showError) { LoginError() } LoginInput() // This call site affects where LoginInput is placed in Composition } @Composable fun LoginInput() { /* ... */ } @Composable fun LoginError() { /* ... */ }
בקטע הקוד שלמעלה, LoginScreen
יקרא ל-composable של LoginError
באופן מותנה, ותמיד יקרא ל-composable של LoginInput
. כל אחד
הקריאה כוללת אתר התקשרות ומיקום מקור ייחודיים, שבהם המהדר ישתמש כדי
לזהות אותו באופן ייחודי.
איור 3. ייצוג של LoginScreen
בהרכבה כשהמצב משתנה ומתבצעת הרכבה מחדש. אם הצבע זהה, סימן שהקמפיין לא עבר עיבוד מחדש.
למרות ש-LoginInput
עברה מקריאה ראשונה לקריאה שנייה, המכונה LoginInput
תישמר בכל הרכבות מחדש. בנוסף,
כי אין למשתמש LoginInput
פרמטרים שהשתנו
מחדש, תדלג על הקריאה ל-LoginInput
על ידי 'כתיבה'.
הוספת מידע נוסף כדי לשפר את הרכבות החכמות מחדש
קריאה ל-composable מספר פעמים תוסיף אותו ל-Composition מספר פעמים גם כן. כשקוראים ל-composable כמה פעמים מאותו אתר קריאה, ל-Compose אין מידע שאפשר להשתמש בו כדי לזהות באופן ייחודי כל קריאה ל-composable הזה. לכן, כדי להבדיל בין המופעים, נעשה שימוש בסדר הביצוע בנוסף לאתר הקריאה. לפעמים זה כל מה שצריך, אבל במקרים מסוימים זה עלול לגרום להתנהגות לא רצויה.
@Composable fun MoviesScreen(movies: List<Movie>) { Column { for (movie in movies) { // MovieOverview composables are placed in Composition given its // index position in the for loop MovieOverview(movie) } } }
בדוגמה שלמעלה, ההצעות לכתיבה משתמשות בסדר הביצוע בנוסף לקריאה
כדי שהמכונה תהיה ייחודית ב-Composition. אם יתווסף movie
חדש
לתחתית הרשימה, התכונה 'כתיבה' יכולה לעשות שימוש חוזר במכונות שכבר
הרכב מאז המיקום שלו ברשימה לא השתנה, ולכן
הקלט movie
זהה במופעים האלה.
איור 4. ייצוג של MoviesScreen
בהרכבה כשמוסיפים רכיב חדש לתחתית הרשימה. MovieOverview
תכנים קומפוזביליים ב
ניתן לעשות שימוש חוזר ביצירה. משמעות צבע זהה ב-MovieOverview
היא תוכן קומפוזבילי
לא קומפוזיציה.
עם זאת, אם הרשימה movies
משתנה על ידי הוספה שלה אל ראש הדף או אל
אמצעי ברשימה, תוך הסרה או שינוי הסדר של פריטים, תגרום להרכבת מחדש
בכל הקריאות ל-MovieOverview
שפרמטר הקלט שלהן השתנה במיקום
חדשה. זה חשוב מאוד אם, לדוגמה, MovieOverview
מאחזר
תמונה של סרט באמצעות אפקט לוואי. אם תבצעו יצירת קומפוזיציה מחדש בזמן שהאפקט מתבצע, הוא יבוטל ויתחיל מחדש.
@Composable fun MovieOverview(movie: Movie) { Column { // Side effect explained later in the docs. If MovieOverview // recomposes, while fetching the image is in progress, // it is cancelled and restarted. val image = loadNetworkImage(movie.url) MovieHeader(image) /* ... */ } }
איור 5. ייצוג של MoviesScreen
ביצירה כשמוסיפים רכיב חדש לרשימה. אי אפשר לעשות שימוש חוזר ב-composables של MovieOverview
, וכל תופעות הלוואי יתחילו מחדש. צבע שונה ב-MovieOverview
מציין שהרכיב המודולרי עוצב מחדש.
באופן אידיאלי, הזהות של מכונה MovieOverview
מקושרת לזהות של movie
שמועברת אליה. אם נזמין מחדש את
של סרטים, באופן אידיאלי נסדר מחדש את המופעים באופן דומה
עץ הרכב במקום להרכיב מחדש כל תוכן קומפוזבילי של MovieOverview
באמצעות
למופע של סרט אחר. פיתוח נייטיב מאפשר לך לציין את זמן הריצה
באילו ערכים להשתמש כדי לזהות חלק מסוים בעץ:
key
קומפוזבילי.
כשעוטפים בלוק קוד בקריאה למפתח שאפשר ליצור ממנו קומפוזיציה עם ערך אחד או יותר שמועברים, הערכים האלה ישולבו וישמשו לזיהוי המופע הזה בתוך הקומפוזיציה. הערך של key
לא חייב להיות ייחודי באופן גלובלי, אלא רק ייחודי בין ההפעלות של הרכיבים הניתנים לקישור באתר הקריאה. בדוגמה הזו, כל movie
צריך לכלול
key
שהוא ייחודי בין movies
; זה בסדר אם הוא משתף את key
עם
תכנים קומפוזביליים אחרים באפליקציה.
@Composable fun MoviesScreenWithKey(movies: List<Movie>) { Column { for (movie in movies) { key(movie.id) { // Unique ID for this movie MovieOverview(movie) } } } }
בעזרת הקוד שלמעלה, גם אם הרכיבים ברשימה ישתנו, Compose יזהה קריאות נפרדות ל-MovieOverview
ויוכל לעשות בהן שימוש חוזר.
איור 6. ייצוג של MoviesScreen
ביצירה כאשר יצירה חדשה
נוסף לרשימה. מכיוון שלרכיבי ה-Compose של MovieOverview
יש מפתחות ייחודיים, Compose מזהה אילו מכונות MovieOverview
לא השתנו ויכול לעשות בהן שימוש חוזר. ההשפעות הצדדיות שלהן ימשיכו לפעול.
לחלק מהרכיבים הניתנים לשילוב יש תמיכה מובנית ברכיב key
הניתן לשילוב. לדוגמה, אפשר לציין key
בהתאמה אישית ב-items
DSL ב-LazyColumn
.
@Composable fun MoviesScreenLazy(movies: List<Movie>) { LazyColumn { items(movies, key = { movie -> movie.id }) { movie -> MovieOverview(movie) } } }
דילוג אם הקלט לא השתנה
במהלך הרכבה מחדש, לחלק מהפונקציות הקומפוזביליות שעומדות בדרישות יכולות להיות המערכת תדלג לגמרי על ההפעלה אם הקלט שלהם לא השתנה של משפטים יחידים,
אפשר לדלג על פונקציה קומפוזבילית אלא אם:
- פונקציה מסוג 'החזרה' היא לא
Unit
- לפונקציה יש הערות עם
@NonRestartableComposable
או@NonSkippableComposable
- פרמטר נדרש הוא מסוג לא יציב
יש מצב מהדר (compiler) ניסיוני, דילוג חזק, שמפחיתה את הדרישה האחרונה.
כדי שסוג ייחשב כיציב, הוא צריך לעמוד בהסכם הבא:
- התוצאה של
equals
עבור שתי מופעים תמיד תהיה זהה ושני מקרים זהים. - אם נכס ציבורי מהסוג משתנה, תישלח הודעה על היצירה.
- גם כל סוגי הנכסים הציבוריים יציבים.
בחוזה הזה יש כמה סוגים נפוצים וחשובים שלפיהם
מהדר לכתובים יתייחס ליציבות, גם אם לא נעשה שימוש מפורש
מסומן כיציב באמצעות ההערה @Stable
:
- כל סוגי הערכים הפרימיטיביים:
Boolean
,Int
,Long
,Float
,Char
וכו'. - מיתרים
- כל סוגי הפונקציות (lambda)
כל הסוגים האלה יכולים לפעול בהתאם לחוזה של היציבות, שלא ניתן לשינוי. מכיוון שסוגי immutable אף פעם לא משתנים, הם אף פעם לא צריכים להודיע ל-Composition על השינוי, ולכן קל הרבה יותר לפעול בהתאם להסכם הזה.
סוג אחד ראוי לציון שהוא יציב אבל ניתן לשינוי הוא הסוג MutableState
של Compose. אם ערך נשמר ב-MutableState
, האובייקט הכולל של המצב הוא
נחשבת כיציבה כי 'כתיבה' יקבל הודעה על כל שינוי
מאפיין .value
של State
.
כשכל הסוגים המועברים כפרמטרים לרכיב מורכב הם יציבים, מתבצעת השוואה בין ערכי הפרמטרים כדי לבדוק אם הם זהים, על סמך המיקום של הרכיב המורכב בעץ של ממשק המשתמש. המערכת תדלג על הרכבת מחדש אם כל הערכים לא השתנו מאז הקריאה הקודמת.
סוג כתיבה נחשב כיציב רק אם ניתן להוכיח אותו. לדוגמה, בדרך כלל נתייחס לממשק כאל לא יציב, וגם לסוגים עם מאפיינים ציבוריים שניתנים לשינוי שההטמעה שלהם עשויה להיות בלתי ניתנת לשינוי.
אם התכונה 'כתיבה' לא יכולה להסיק שסוג מסוים הוא יציב, אבל אתם רוצים לאלץ
אפשר לכתוב 'אני רוצה' כדי להתייחס לגרסה יציבה ולסמן אותה באמצעות
@Stable
.
// Marking the type as stable to favor skipping and smart recompositions. @Stable interface UiState<T : Result<T>> { val value: T? val exception: Throwable? val hasError: Boolean get() = exception != null }
בקטע הקוד שלמעלה, מכיוון ש-UiState
הוא ממשק, Compose יכול בדרך כלל להתייחס לסוג הזה כאל סוג לא יציב. על ידי הוספת @Stable
מציינים ל'כתיבה' שהסוג הזה יציב, והוא מאפשר ל'כתיבה' ליהנות
הרכבים מחדש חכמים. פירוש הדבר הוא גם ש'כתיבה' תטפל בכל
או הטמעות יציבות, אם הממשק משמש כסוג הפרמטר.
מומלץ עבורך
- הערה: טקסט הקישור מוצג כאשר JavaScript מושבת
- State ו-Jetpack פיתוח נייטיב
- תופעות לוואי ב-Compose
- שמירת המצב של ממשק המשתמש בחלונית הכתיבה