בדף הזה תלמדו על מחזור החיים של תוכן קומפוזבילי האופן שבו התכונה Compose קובע אם תוכן קומפוזבילי צריך להרכיב מחדש.
סקירה כללית של מחזור החיים
כפי שצוין במסמכים לגבי ניהול מצב, הרכב מתאר את ממשק המשתמש של האפליקציה ומופק על ידי הרצה של תכנים קומפוזביליים. הרכב הוא מבנה עץ של התכנים הקומפוזביליים שמתארים את ממשק המשתמש שלכם.
כש-Jetpack פיתוח נייטיב מריץ את התכנים הקומפוזביליים בפעם הראשונה, במהלך האתחול הרכב, הוא יעקוב אחרי התכנים הקומפוזביליים שאתם קוראים כדי לתאר ממשק המשתמש ביצירה. לאחר מכן, כשמצב האפליקציה משתנה, Jetpack כתיבת לוחות זמנים של יצירה מחדש. הרכבה מחדש היא כש-Jetpack פיתוח נייטיב מפעיל מחדש את התכנים הקומפוזביליים שייתכן שהשתנו בתגובה לשינויים במצב, ולאחר מכן מעדכנת את היצירה כך שתשקף את השינויים, אם ישנם.
ניתן ליצור יצירה מוזיקלית רק אם ליצור יצירה ראשונית ולעדכן אותה על ידי חיבור מחדש. הדרך היחידה לשנות יצירה מוזיקלית היא באמצעות הרכבה מחדש.
איור 1. מחזור החיים של חומר קומפוזבילי ביצירה הוא מזין את יצירה מוזיקלית, הרכבה מחדש 0 פעמים או יותר, ויוצאת מהיצירה.
הרכבה מחדש בדרך כלל מופעלת על ידי שינוי
אובייקט State<T>
. אימייל חדש
שעוקבת אחריה ומריצה את כל התכנים הקומפוזביליים במערך היצירה שקוראת
State<T>
מסוים, וכל תוכן קומפוזבילי שהוא קורא לתוכן שלא יכול
דילגת.
אם תוכן קומפוזבילי נקרא כמה פעמים, יצירה מוזיקלית. לכל קריאה יש מחזור חיים משלה בקטע 'יצירה'.
@Composable fun MyComposable() { Column { Text("Hello") Text("World") } }
איור 2. ייצוג של MyComposable
ביצירה. אם
נקרא 'קומפוזביליות' כמה פעמים,
יצירה מוזיקלית. שרכיב בצבע אחר מצביע על כך שהוא
למופע נפרד.
האנטומיה של חומר קומפוזבילי ביצירה
המופע של תוכן קומפוזבילי בקטע 'יצירה' מזוהה על ידי אתר הקריאה שלו. המהדר לכתיבה מתייחס לכל אתר שיחות כאתר נפרד. קריאה לתכנים קומפוזביליים מכמה אתרי התקשרות ייצרו כמה מופעים של התוכן הקומפוזבילי יצירה מוזיקלית.
אם במהלך יצירה מחדש של קומפוזביליות, קריאה קומפוזבילית שונה מזו במהלך היצירה הקודמת, 'פיתוח נייטיב' יזהה אילו תכנים קומפוזביליים שנקראו או לא נקראו, ושל התכנים הקומפוזביליים שנקראו יצירות מוזיקליות, 'פיתוח נייטיב' ימנע הרכבה מחדש, אם הקלט לא שונה.
שימור הזהות הוא חיוני כדי לשייך תופעות לוואי לתכנים הקומפוזביליים שלהם, כדי שיוכלו להשלים בהצלחה במקום להפעיל אותן מחדש בכל חיבור מחדש.
עיינו בדוגמה הבאה:
@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
יקרא באופן מותנה את
התוכן הקומפוזבילי LoginError
, ותמיד יקרא לתוכן הקומפוזבילי LoginInput
. כל אחד
הקריאה כוללת אתר התקשרות ומיקום מקור ייחודיים, שבהם המהדר ישתמש כדי
לזהות אותו באופן ייחודי.
איור 3. ייצוג של LoginScreen
ביצירה כאשר המדינה
ומתרחשת הרכבה מחדש. אם הצבע שונה, הסרטון לא הורכב מחדש.
למרות שLoginInput
כבר לא התבקש/ה להתקשר לפני כן,
המופע LoginInput
יישמר בהרכבת מחדש. בנוסף,
כי אין למשתמש LoginInput
פרמטרים שהשתנו
מחדש, תדלג על הקריאה ל-LoginInput
על ידי 'כתיבה'.
הוספת מידע כדי לעזור הרכבים מחדש חכמים
קריאה של חומר קומפוזבילי מספר פעמים תוסיף אותו ליצירה מספר פעמים, נו. כשקוראים לתוכן קומפוזבילי כמה פעמים מאותו אתר שיחה, צריך לכתוב הודעה אין מידע שמזהה כל קריאה לתוכן הקומפוזבילי הזה באופן ייחודי, אז צו הביצוע משמש בנוסף לאתר ההתקשרות כדי לשמור לכל אירוע בנפרד. לפעמים כל מה שנחוץ הוא ההתנהגות הזו, אבל בחלק מהמקרים במקרים מסוימים היא עלולה לגרום להתנהגות לא רצויה.
@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) } } }
בדוגמה שלמעלה, ההצעות לכתיבה משתמשות בסדר הביצוע בנוסף לקריאה
כדי שהמכונה תהיה ייחודית ביצירה. אם יתווסף 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
ביצירה כאשר יצירה חדשה
נוסף לרשימה. אי אפשר לעשות שימוש חוזר ב-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) } } } }
במצב הזה, גם אם הרכיבים ברשימה משתנים, התכונה 'כתיבה' מזהה
קריאות נפרדות אל MovieOverview
ויכולים לעשות בהן שימוש חוזר.
איור 6. ייצוג של MoviesScreen
ביצירה כאשר יצירה חדשה
נוסף לרשימה. בגלל שלתוכן הקומפוזבילי MovieOverview
יש
המפתחות, 'כתיבה' מזהה אילו מופעים של MovieOverview
לא השתנו,
יכולים לעשות בהם שימוש חוזר, תופעות הלוואי שלהם ימשיכו לפעול.
לחלק מהתכנים הקומפוזביליים יש תמיכה מובנית בתוכן הקומפוזבילי של key
. לדוגמה,
LazyColumn
מקבל ציון של key
מותאם אישית ב-DSL items
.
@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
וכו' - מיתרים
- כל סוגי הפונקציות (lambdas)
כל הסוגים האלה יכולים לפעול בהתאם לחוזה של היציבות, שלא ניתן לשינוי. הסוגים שאינם ניתנים לשינוי אף פעם לא משתנים, ולכן הם אף פעם לא צריכים להודיע הרכב השינוי, כך שקל יותר לעמוד בחוזה הזה.
סוג אחד בולט שהוא יציב אבל ניתן לשינוי הוא MutableState
של 'כתיבה'
מהסוג הזה. אם ערך נשמר ב-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
הוא ממשק, האפשרות 'כתיבה' יכולה
בדרך כלל מחשיבים את הסוג הזה כלא יציב. על ידי הוספת @Stable
מציינים ל'כתיבה' שהסוג הזה יציב, והוא מאפשר ל'כתיבה' ליהנות
הרכבים מחדש חכמים. פירוש הדבר הוא גם ש'כתיבה' תטפל בכל
או הטמעות יציבות, אם הממשק משמש כסוג הפרמטר.
מומלץ עבורך
- הערה: טקסט הקישור מוצג כאשר JavaScript מושבת
- State ו-Jetpack פיתוח נייטיב
- תופעות לוואי ב-Compose
- שמירת המצב של ממשק המשתמש בחלונית הכתיבה