מחזור החיים של מקטע

לכל מכונה של Fragment יש מחזור חיים משלה. כשמשתמש מנווט באפליקציה ומקיים אינטראקציה איתה, הקטעים עוברים מצבים שונים במהלך מחזור החיים שלהם, כאשר הם מתווספים, מוסרים, נכנסים למסך או יוצאים ממנו.

כדי לנהל את מחזור החיים, Fragment מטמיע את LifecycleOwner, ומציג אובייקט Lifecycle שאפשר לגשת אליו באמצעות השיטה getLifecycle().

כל מצב אפשרי של Lifecycle מיוצג במערך הערכים Lifecycle.State.

כשמפתחים את Fragment מעל Lifecycle, אפשר להשתמש בשיטות ובכיתות שזמינות לטיפול במחזורי חיים באמצעות רכיבים מודעים למחזור החיים. לדוגמה, אפשר להציג את המיקום של המכשיר במסך באמצעות רכיב שמתחשב במחזור החיים. הרכיב הזה יכול להתחיל להאזין באופן אוטומטי כשהקטע הופך לפעיל ולהפסיק כשהקטע עובר למצב לא פעיל.

כחלופה לשימוש ב-LifecycleObserver, הכיתה Fragment כוללת שיטות קריאה חוזרת (callback) שתואמות לכל אחד מהשינויים במחזור החיים של קטע הקוד. אלה כוללים את onCreate(),‏ onStart(),‏ onResume(),‏ onPause(),‏ onStop() ו-onDestroy().

לתצוגה של קטע יש Lifecycle נפרד שמנוהל בנפרד מ-Lifecycle של הקטע. לכל קטע יש LifecycleOwner משלו, שאפשר לגשת אליו באמצעות getViewLifecycleOwner() או getViewLifecycleOwnerLiveData(). הגישה ל-Lifecycle של התצוגה שימושית במצבים שבהם רכיב עם תמיכה במחזור חיים צריך לבצע פעולות רק בזמן שהתצוגה של החלק הקיימת, למשל מעקב אחרי LiveData שמיועד להופיע רק במסך.

בנושא הזה נסביר בפירוט את מחזור החיים של Fragment, נציג חלק מהכללים שקובעים את מצב מחזור החיים של קטע קוד ונסביר את הקשר בין המצבים של Lifecycle לבין פונקציות ה-callbacks של מחזור החיים של קטע הקוד.

קטעים ומנהל הקטעים

כשיוצרים מופע של קטע קוד, הוא מתחיל במצב INITIALIZED. כדי שקטע קוד יוכל לעבור את שאר שלבי מחזור החיים שלו, צריך להוסיף אותו ל-FragmentManager. ה-FragmentManager אחראי לקבוע באיזה מצב צריך להיות הפלח שלו, ולאחר מכן להעביר אותו למצב הזה.

מעבר למחזור החיים של הקטעים, FragmentManager אחראי גם לקישור קטעים לפעילות המארחת שלהם ולניתוק שלהם כשהקטע כבר לא בשימוש. לכיתה Fragment יש שתי שיטות קריאה חוזרת (callbacks), onAttach() ו-onDetach(), שאפשר לשנות כדי לבצע פעולות כשאחד מהאירועים האלה מתרחש.

פונקציית ה-callback של onAttach() מופעלת כשהקטע נוסף ל-FragmentManager ומצורף לפעילות המארחת שלו. בשלב הזה, החלק הפעיל וה-FragmentManager מנהלים את מצב מחזור החיים שלו. בשלב הזה, שיטות FragmentManager כמו findFragmentById() מחזירות את הקטע הזה.

תמיד קוראים ל-onAttach() לפני כל שינוי במצב של מחזור החיים.

פונקציית הקריאה החוזרת onDetach() מופעלת כשהקטע הוסר מ-FragmentManager והתנתק מהפעילות המארחת שלו. הפלח כבר לא פעיל ואי אפשר לאחזר אותו באמצעות findFragmentById().

onDetach() תמיד נקרא אחרי שינויים במצב של מחזור החיים.

חשוב לדעת שהקריאות החוזרות האלה לא קשורות לשיטות attach() ו-detach() של FragmentTransaction. מידע נוסף על השיטות האלה זמין במאמר פיצול עסקאות.

מצבים של מחזור חיים של קטעי קוד וקריאות חוזרות

כדי לקבוע את מצב מחזור החיים של קטע קוד, FragmentManager מתייחס לגורמים הבאים:

  • המצב המקסימלי של קטע נקבע לפי FragmentManager שלו. הפלח לא יכול להתקדם מעבר למצב של FragmentManager שלו.
  • כחלק מ-FragmentTransaction, אפשר להגדיר מצב מקסימלי של מחזור חיים בחלק באמצעות setMaxLifecycle() .
  • מצב מחזור החיים של קטע לא יכול להיות גבוה יותר מזה של הרכיב ההורה שלו. לדוגמה, צריך להפעיל את הפעילות או את קטע הקוד ההורה לפני שמפעילים את קטעי הקוד הצאצאים. באופן דומה, צריך להפסיק את הפעולה של קטעי הקוד הצאצאים לפני הפעולה של הפעילות או של קטע הקוד ההורה.
מצבי מחזור החיים של הפלח והקשר שלהם לקריאות החזרה (callbacks) של מחזור החיים של הפלח ולמחזור החיים של התצוגה של הפלח
איור 1. מצבי ה-Fragment Lifecycle והקשר שלהם גם ל-callbacks של מחזור החיים של ה-Fragment וגם לתצוגה Lifecycle של ה-Fragment.

באיור 1 מוצגים כל המצבים של Lifecycle ב-fragment, והאופן שבו הם קשורים גם ל-callbacks של מחזור החיים של ה-fragment וגם לתצוגה Lifecycle של ה-fragment.

ככל שהקטע מתקדם במחזור החיים שלו, הוא עובר למעלה ולמטה במצבים שלו. לדוגמה, קטע שמתווסף לחלק העליון של סטאק הקודם עובר למעלה מ-CREATED ל-STARTED ל-RESUMED. לעומת זאת, כשקטע מודחק מהמקבץ הקודם, הוא עובר למטה דרך המצבים האלה, מ-RESUMED ל-STARTED ל-CREATED ולבסוף ל-DESTROYED.

מעברים למצבים גבוהים יותר

כשהקטע עובר למצבים הבאים במחזור החיים שלו, הוא קורא קודם ל-callback המשויך למחזור החיים של המצב החדש. בסיום הקריאה החוזרת, האירוע הרלוונטי Lifecycle.Event יועבר לצופים על ידי Lifecycle של החלק, ולאחר מכן על ידי התצוגה Lifecycle של החלק, אם נוצרה.

מקטע נוצר

כשהמקטע מגיע למצב CREATED, הוא כבר נוסף ל-FragmentManager והשיטה onAttach() כבר הוזמנה.

זהו המקום המתאים לשחזור כל מצב ששמור ומשויך לקטע עצמו באמצעות SavedStateRegistry של הקטע. חשוב לזכור שהתצוגה של החלק הקטן לא נוצרה בשלב הזה, וכל מצב שמשויך לתצוגה של החלק הקטן צריך להיות משוחזר רק אחרי יצירת התצוגה.

המעבר הזה מפעיל את הפונקציה החוזרת onCreate(). פונקציית הקריאה החוזרת מקבלת גם את הארגומנט savedInstanceState Bundle שמכיל את כל המצבים שנשמרו בעבר על ידי onSaveInstanceState(). חשוב לזכור של-savedInstanceState יש ערך null בפעם הראשונה שהקטע נוצר, אבל הוא תמיד לא null ביצירות חוזרות, גם אם לא מבטלים את ברירת המחדל של onSaveInstanceState(). פרטים נוספים זמינים במאמר שמירה של מצב באמצעות קטעים.

אירועי CREATED ו-View INITIALIZED של מקטע

התצוגה Lifecycle של החלק המוצג נוצרת רק כש-Fragment מספק מופע חוקי של View. ברוב המקרים, אפשר להשתמש במגדירי הקטעים שמקבלים @LayoutId, שמנפח את התצוגה באופן אוטומטי בזמן המתאים. אפשר גם לשנות את ברירת המחדל של onCreateView() כדי ליצור או לנפח את התצוגה של החלק באופן פרוגרמטי.

אם View שמשמש ליצירת המופע של תצוגת הפלח הוא לא null, ה-View הזה מוגדר בפלח וניתן לאחזר אותו באמצעות getView(). לאחר מכן, הערך של getViewLifecycleOwnerLiveData() מתעדכן בערך החדש של INITIALIZED LifecycleOwner שתואם לתצוגה של הקטע. באותו זמן מתבצעת גם קריאה חוזרת במחזור החיים של onViewCreated().

זהו המקום המתאים להגדרת המצב הראשוני של התצוגה, להתחיל לעקוב אחרי מכונות LiveData שהקריאות החוזרות שלהן מעדכנות את התצוגה של החלק, ולהגדיר מתאמים לכל מכונות RecyclerView או ViewPager2 בתצוגה של החלק.

יצירת קטעים ותצוגות

אחרי יצירת התצוגה של החלק, מצב התצוגה הקודם, אם יש כזה, משוחזר, ואז Lifecycle של התצוגה מועבר למצב CREATED. הבעלים של מחזור החיים של התצוגה גם משדר את האירוע ON_CREATE למשתמשי הצפייה. כאן צריך לשחזר כל מצב נוסף שמשויך לתצוגה של החלק.

המעבר הזה גם מפעיל את הפונקציה החוזרת (callback) onViewStateRestored().

'התחלה' של פרגמנט ותצוגה

מומלץ מאוד לקשר רכיבים מודעים למחזור חיים למצב STARTED של קטע, כי המצב הזה מבטיח שהתצוגה של הקטע זמינה, אם נוצרה כזו, ושבטוח לבצע FragmentTransaction על הצאצא FragmentManager של הקטע. אם התצוגה של ה-fragment לא null, התצוגה Lifecycle של ה-fragment תועבר אל STARTED מיד אחרי שה-Lifecycle של ה-fragment יועבר אל STARTED.

כשהקטע הופך ל-STARTED, מתבצעת קריאה חוזרת (callback) ל-onStart().

'הפרגמנט והתצוגה הומשכו'

כשהקטע גלוי, כל ההשפעות של Animator ושל Transition מסתיימות, והקטע מוכן לאינטראקציה של המשתמש. הערך של Lifecycle של החלק המודגש עובר למצב RESUMED, והפונקציה הלא סטטית onResume() מופעלת.

המעבר ל-RESUMED הוא האות המתאים שמציין שהמשתמש יכול עכשיו לקיים אינטראקציה עם החלק. בקטעי קוד שאינם RESUMED אסור להגדיר באופן ידני את המיקוד בתצוגות שלהם או לנסות לטפל בחשיפה של שיטת הקלט.

מעברים למצבים נמוכים יותר

כשקטע נע למטה למצב נמוך יותר במחזור החיים, האירוע הרלוונטי Lifecycle.Event מונפק למתבוננים על ידי התצוגה Lifecycle של הקטע, אם נוצרה, ואחריו Lifecycle של הקטע. אחרי שהאירוע של מחזור החיים של החלק מופעל, החלק קורא ל-callback המשויך של מחזור החיים.

'התחלה' של פרגמנט ותצוגה

כשהמשתמש מתחיל לצאת מהקטע, ובזמן שהקטע עדיין גלוי, ה-Lifecycle של הקטע ושל התצוגה שלו מועברים חזרה למצב STARTED ומפיצים את האירוע ON_PAUSE למשתמשי הצפייה שלהם. לאחר מכן, החלק מפעיל את פונקציית הקריאה החוזרת שלו, onPause().

יצירת קטעים ותצוגות

כשהקטע כבר לא גלוי, ה-Lifecycle של הקטע ושל התצוגה שלו מועברים למצב CREATED ומפיצים את האירוע ON_STOP למשתמשי המעקב שלהם. מעבר המצב הזה מופעל לא רק על ידי הפסקת הפעילות או הפלח ההורה, אלא גם על ידי שמירת המצב על ידי הפעילות או הפלח ההורה. ההתנהגות הזו מבטיחה שהאירוע ON_STOP יופעל לפני שמצב הפלח נשמר. לכן, האירוע ON_STOP הוא הנקודה האחרונה שבה אפשר לבצע FragmentTransaction על הצאצא FragmentManager.

כפי שמוצג באיור 2, הסדר של קריאת החזרה (callback) של onStop() ושמירת המצב באמצעות onSaveInstanceState() משתנה בהתאם לרמת ה-API. בכל רמות ה-API שקדמו ל-API 28, onSaveInstanceState() מופעל לפני onStop(). ברמת API 28 ואילך, סדר הקריאה הפוך.

הבדלים בסדר הקריאה של onStop() ו-onSaveInstanceState()
איור 2. הבדלים בסדר הקריאה של onStop() ושל onSaveInstanceState().

אירוע CREATED של קטע קוד ותצוגה שהושמדה

אחרי שכל האנימציות והמעברים של היציאה מסתיימים, והתצוגה של החלק מנותקת מהחלון, התצוגה Lifecycle של החלק מועברת למצב DESTROYED ומפיצה את האירוע ON_DESTROY למתבוננים שלה. לאחר מכן, ה-fragment מפעיל את הפונקציה החוזרת שלו, onDestroyView(). בשלב הזה, התצוגה של החלק המודגש הגיעה לסוף מחזור החיים שלה ו-getViewLifecycleOwnerLiveData() מחזירה את הערך null.

בשלב הזה צריך להסיר את כל ההפניות לתצוגה של החלק, כדי לאפשר איסוף אשפה של התצוגה של החלק.

מקטע DESTROYED

אם הפלח יוסר או אם FragmentManager נהרס, הערך של Lifecycle בפלח יועבר לסטטוס DESTROYED והוא ישלח את האירוע ON_DESTROY למשתמשי הצפייה שלו. לאחר מכן, ה-fragment מפעיל את הפונקציה החוזרת שלו, onDestroy(). בשלב הזה, הקטע הגיע לסוף מחזור החיים שלו.

מקורות מידע נוספים

למידע נוסף על מחזור החיים של הפלח, תוכלו לעיין במקורות המידע הנוספים הבאים.

מדריכים

בלוגים