Lab Lab זה הוא חלק מקורס Android Kotlin Fundamentals. כדי להפיק את המקסימום מהקורס הזה, יש לפעול ברצף לפי קודי שיעור ה-Lab. כל שיעורי Lab של הקורסים מופיעים בדף הנחיתה של Lab Kotlin Fundamentals ל-Android Lab.
מבוא
במעבדה האחרונה, למדתם על מחזורי החיים של Activity
ו-Fragment
ובחנתם את השיטות שנקראות כאשר מצב מחזור החיים משתנה בפעילויות ובמקטעים. בשיעור Lab זה, תעיינו במחזור החיים של הפעילות בפירוט רב יותר. אתם מקבלים גם מידע על ספריית מחזור החיים של Android Jetpack&&39, שעוזרת לכם לנהל אירועים במחזור החיים באמצעות קוד שמאורגן בצורה טובה יותר וקלה יותר לתחזוקה.
מה כבר צריך לדעת
- מהי פעילות ואיך ליצור פעילות באפליקציה.
- היסודות של מחזורי החיים של
Activity
ו-Fragment
, וקריאות חוזרות (callbacks) שמופעלות כשפעילות עוברת בין מצבים. - איך לעקוף את שיטות הקריאה החוזרת של מחזור החיים של
onCreate()
ושלonStop()
כדי לבצע פעולות בזמנים שונים בפעילות או במחזור החיים של שבר.
מה תלמדו
- איך להגדיר, להפעיל ולהפסיק חלקים באפליקציה בקריאות החוזרות של מחזור החיים.
- איך להשתמש בספרייה של מחזור החיים של Android כדי ליצור כלי צפייה במחזור החיים, וכדי להקל על הניהול של הפעילות ומחזור החיים של שבר.
- איך כיבויים של עיבוד Android משפיעים על הנתונים באפליקציה, ואיך לשמור ולשחזר את הנתונים באופן אוטומטי כש-Android סוגרת את האפליקציה שלכם.
- איך סבב המכשירים ושינויים אחרים בתצורות יוצרים שינויים במצב מחזור החיים ומשפיעים על מצב האפליקציה.
מה צריך לעשות
- משנים את אפליקציית DessertClicker כך שתכלול פונקציית טיימר, ומתחילים ומפסיקים את הטיימר הזה במחזור החיים של הפעילות.
- עליך לשנות את האפליקציה לשימוש בספריית מחזור החיים של Android, ולהמיר את הכיתה ל-
DessertTimer
לצופה במחזור החיים. - כדאי להגדיר את גשר ניפוי הבאגים ב-Android (
adb
) ולהשתמש בו כדי לדמות את כיבוי התהליך של האפליקציה ואת הקריאה החוזרת (callback) על מחזור החיים. - צריך להשתמש בשיטה
onSaveInstanceState()
כדי לשמור נתוני אפליקציות שעשויים ללכת לאיבוד אם האפליקציה תיסגר באופן בלתי צפוי. יש להוסיף קוד כדי לשחזר את הנתונים האלה כאשר האפליקציה מופעלת מחדש.
ב-codelab זה תרחיבו את אפליקציית DessertClicker מ-codelab הקודם. מוסיפים טיימר ברקע ולאחר מכן ממירים את האפליקציה לשימוש בספריית מחזור החיים של Android.
במעבדת הקוד הקודמת למדתם איך לעקוב אחר הפעילות ומחזורי החיים של קטעים מקוטעים, על ידי ביטול קריאות שונות במהלך מחזור החיים והתחברות כאשר המערכת מפעילה את הקריאות החוזרות האלה. במשימה הזו, תתמקדו בדוגמה מורכבת יותר של ניהול משימות במחזור החיים באפליקציה DessertClicker. אתם משתמשים בטיימר כדי להדפיס הצהרת יומן בכל שנייה, עם ספירת מספר השניות שהיא פועלת.
שלב 1: הגדרת טיימר לקינוח
- פותחים את אפליקציית DessertClicker ממעבדת הקוד האחרונה. (ניתן להוריד את DessertClickerLogs כאן אם האפליקציה לא מותקנת במכשיר).
- בתצוגה Project (פרויקט), מרחיבים את java > com.example.android.dessertclicker ופותחים את
DessertTimer.kt
. הערה: נכון לעכשיו, כל הקוד מוזכר בתגובה, ולכן הוא לא פועל כחלק מהאפליקציה. - בוחרים את כל הקוד בחלון העורך. בוחרים באפשרות Code > תגובה עם Line Response או מקישים על
Control+/
(Command+/
ב-Mac). הפקודה הזו לא מבטלת את הקוד כולו בקובץ. (ייתכן שמערכת Android Studio תציג שגיאות הפניה שלא נפתרו עד לבנות את האפליקציה מחדש). - שימו לב: הכיתה
DessertTimer
כוללת אתstartTimer()
ואתstopTimer()
, שמפעילים ומפסיקים את הטיימר. כאשרstartTimer()
פועל, הטיימר מדפיס הודעת יומן לכל שנייה, עם המספר הכולל של השניות שבהן הזמן פעלו. השיטהstopTimer()
מפסיקה את הטיימר ואת דוחות היומן.
- פתיחת
MainActivity.kt
בחלק העליון של הכיתה, מתחת למשתנהdessertsSold
, מוסיפים משתנה לטיימר:
private lateinit var dessertTimer : DessertTimer;
- יש לגלול למטה אל
onCreate()
וליצור אובייקטDessertTimer
חדש, מיד לאחר הקריאה אלsetOnClickListener()
:
dessertTimer = DessertTimer()
עכשיו, כשיש לכם אובייקט קינוח, כדאי לשקול איפה כדאי להתחיל ולהפסיק את הטיימר כדי להפעיל אותו רק כשהפעילות מוצגת במסך. בשלבים הבאים נבחן את האפשרויות השונות.
שלב 2: מפעילים את הטיימר ומפסיקים אותו
השיטה onStart()
נקראת ממש לפני שהפעילות מוצגת. השיטה של onStop()
נקראת לאחר שהפעילות מפסיקה להופיע. הקריאה החוזרת הזו נראית כמו מועמדים טובים להפעלה ולהפסקה של הטיימר.
- בשיעור
MainActivity
, מתחילים את הטיימר בקריאה החוזרת (onStart()
):
override fun onStart() {
super.onStart()
dessertTimer.startTimer()
Timber.i("onStart called")
}
- הפסקת הטיימר ב
onStop()
:
override fun onStop() {
super.onStop()
dessertTimer.stopTimer()
Timber.i("onStop Called")
}
- הידור והפעלה של האפליקציה. ב-Android Studio, לוחצים על החלונית Logcat . בתיבת החיפוש של Logcat, מזינים
dessertclicker
. מסנן זה יסנן לפי הכיתותMainActivity
וגםDessertTimer
. חשוב לציין שברגע שהאפליקציה מתחילה, הטיימר מתחיל לפעול מיד. - לוחצים על הלחצן הקודם ומציינים שהטיימר מפסיק שוב. הטיימר מפסיק כי הפעילות וגם הטיימר ששולטים הרוסים.
- יש להשתמש במסך התגובות האחרונות כדי לחזור לאפליקציה. לתשומת ליבך, ב-Logcat הטיימר מופעל מחדש מ-0.
- לוחצים על הלחצן שיתוף. שימו לב שהטיימר ב-Logcat עדיין פועל.
- לוחצים על הלחצן דף הבית. שימו לב ב-Logcat שהטיימר מפסיק לפעול.
- מסך 'אחרונים' מאפשר לחזור לאפליקציה. חשוב לשים לב ב-Logcat שהטיימר מופעל שוב מהמקום שבו הופסק.
- בעוד
MainActivity
, יש להוסיף את השיחה אלstopTimer()
בשיטתonStop()
. התגובהstopTimer()
מציגה את המקרה שבו התחלת פעולה באפליקציהonStart()
, אבל חשוב לזכור לעצור אותה שוב בעודonStop()
. - הידור והפעלה של האפליקציה, ולחיצה על לחצן דף הבית אחרי שהטיימר מתחיל. האפליקציה פועלת ברקע, אבל הטיימר פועל וממשיך במשאבי המערכת. המשך ההפעלה של הטיימר הוא דליפת זיכרון לאפליקציה שלך, וככל הנראה לא ההתנהגות הרצויה.
הדפוס הכללי הוא שכאשר מגדירים או מתחילים משהו בקריאה חוזרת (callback), מתבצעת עצירה או הסרה של הפעולה הזו בקריאה החוזרת (callback). כך אפשר להימנע מלהפעיל פריטים כשהם לא צריכים אותם יותר.
- יש לבטל את ההערה על השורה ב-
onStop()
שבה מפסיקים את הטיימר. - יש לחתוך ולהדביק את שיחת
startTimer()
מ-onStart()
ל-onCreate()
. השינוי הזה מדגים את המקרה שבו גם אתה מפעיל ומשאב משאב ב-onCreate()
, במקום להשתמש ב-onCreate()
כדי להפעיל אותו וגם ב-onStart()
כדי להפעיל אותו. - מכינים את האפליקציה ומפעילים אותה. שימו לב שהטיימר מתחיל לפעול, כפי שציפיתם.
- לוחצים על 'דף הבית' כדי להפסיק את השימוש באפליקציה. הטיימר מפסיק לפעול, כפי שציפיתם.
- במסך האחרון, אפשר להשתמש במסך האחרון כדי לחזור לאפליקציה. לתשומת ליבך, הטיימר לא מתחיל שוב במקרה הזה, כי
onCreate()
מופעל רק כשהאפליקציה מתחילה – היא לא נשלחת כשאפליקציה חוזרת לחזית.
נקודות עיקריות שכדאי לזכור:
- כשתגדירו משאב בקריאה חוזרת במחזור החיים, תצטרכו לנתק גם את המשאב.
- לבצע הגדרה וניתוק בשיטות מתאימות.
- אם מגדירים משהו ב-
onStart()
, צריך להפסיק או לקרוס אותו שוב בעודonStop()
.
באפליקציית DessertClicker, קל לראות שאם התחלת את הטיימר ב-onStart()
, עליך להפסיק את הטיימר ב-onStop()
. יש רק טיימר אחד, לכן קשה לזכור את הטיימר.
באפליקציה מורכבת יותר של Android, אפשר להגדיר הרבה דברים ב-onStart()
או ב-onCreate()
ולנתק אותם מ-onStop()
או מ-onDestroy()
. לדוגמה, ייתכן שיהיו לכם אנימציות, מוזיקה, חיישנים או טיימרים שאתם צריכים גם להגדיר ולפרוע מהם, וגם להתחיל ולהפסיק את השידור. אם שוכחים את התג, הבאג עלול לגרום לבאגים ולכאבי ראש.
ספריית מחזור החיים, שהיא חלק מ-Android Jetpack, מפשטת את המשימה הזו. הספרייה שימושית במיוחד במקרים שבהם צריך לעקוב אחר חלקים רבים בתנועה, חלקם במצבים שונים של מחזור החיים. הספרייה מתחלפת באופן שבו מחזורי החיים פועלים: בדרך כלל, הפעילות או הקטע אומרים לרכיב (למשל DessertTimer
) מה צריך לעשות כשמתרחשת קריאה חוזרת במחזור החיים. אבל כשמשתמשים בספריית מחזור החיים, הרכיב עצמו צופה בשינויים במחזור החיים, ואז מבצע את הפעולות הנדרשות כאשר השינויים האלה מתרחשים.
ספריית מחזור החיים כוללת שלושה חלקים עיקריים:
- בעלים של מחזור חיים, שהם הרכיבים שיש להם (ולכן גם "owner").
Activity
וגםFragment
הם בעלים של מחזור חיים. בעלי מחזור החיים מטמיעים את הממשקLifecycleOwner
. - המחלקה
Lifecycle
, שמחזיקה את המצב בפועל של בעלי מחזור החיים ומפעילה אירועים כאשר מתרחשים שינויים במחזור החיים. - צופים במחזור החיים, המתעדים את מצב מחזור החיים ומבצעים משימות כאשר מחזור החיים משתנה. צופים במחזור חיים מטמיעים את הממשק
LifecycleObserver
.
במשימה הזו, אתם ממירים את האפליקציה DessertClicker לשימוש בספרייה של מחזור החיים של Android, ולומדים איך הספרייה מאפשרת לנהל בקלות את הפעילות ב-Android ואת מחזורי החיים של מקטעים.
שלב 1: הופכים את טיימר הקינוח למחזור חיים
חלק מרכזי מספריית מחזור החיים הוא הקונספט של תצפית על מחזור חיים. תצפית מאפשרת לכיתות (כמו DessertTimer
) לדעת על הפעילות או על מחזור החיים של מקטע, ולהתחיל ולהפסיק את עצמן בתגובה לשינויים במצבי מחזור החיים האלה. עם מכשיר תצפית על מחזור החיים, אפשר להסיר את האחריות להפעלה ולעצירה של אובייקטים מהפעילות ומשיטות המקטע.
- פותחים את הכיתה ב-
DesertTimer.kt
. - משנים את חתימת הכיתה של הכיתה ב-
DessertTimer
כדי להיראות כך:
class DessertTimer(lifecycle: Lifecycle) : LifecycleObserver {
הגדרת הכיתה החדשה עושה זאת:
- המבנה מחזיק אובייקט
Lifecycle
, שהוא מחזור החיים שהטיימר צופה בו. - ההגדרה של הכיתה מטמיעה את הממשק של
LifecycleObserver
.
- מתחת למשתנה
runnable
, מוסיפים בלוקinit
להגדרת הכיתה. בבלוקinit
, יש להשתמש בשיטהaddObserver()
כדי לחבר את האובייקט של מחזור החיים שהועבר מהבעלים (הפעילות) לכיתה הזו (הצופה).
init {
lifecycle.addObserver(this)
}
- יש להוסיף הערה ל
startTimer()
באמצעות ה@OnLifecycleEvent annotation
, ולהשתמש באירועON_START
של מחזור החיים. כל אירועי מחזור החיים שצופים במחזור החיים יכולים לראות הם בקטגוריהLifecycle.Event
.
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun startTimer() {
- חוזרים על הפעולה של
stopTimer()
, באמצעות האירועON_STOP
:
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stopTimer()
שלב 2: משנים את הפעילות הראשית
הכיתה שלך MainActivity
היא כבר הבעלים של מחזור החיים דרך ירושה, כי ה-Superclass FragmentActivity
מטמיעה את LifecycleOwner
. לכן, לא נדרשת כל פעולה מצידכם כדי לעדכן את הפעילות שלכם במחזור החיים. צריך רק להעביר את אובייקט מחזור החיים של הפעילות לבונה של DessertTimer
.
- פתיחת
MainActivity
בשיטהonCreate()
, יש לשנות את האתחול שלDessertTimer
כך שיכלול אתthis.lifecycle
:
dessertTimer = DessertTimer(this.lifecycle)
הנכס lifecycle
של הפעילות מכיל את האובייקט Lifecycle
שבבעלותו.
- יש להסיר את השיחה אל
startTimer()
בעודonCreate()
, ואת השיחה אלstopTimer()
בעודonStop()
. אין צורך לומר ל-DessertTimer
מה לעשות בפעילות הזו יותר, כיDessertTimer
צופה עכשיו במחזור החיים עצמו ומתעדכן באופן אוטומטי כאשר מצב מחזור החיים משתנה. כל מה שצריך לעשות בהתקשרות החוזרת הזו הוא לתעד הודעה. - הידור והפעלה של האפליקציה ופתיחת יומן הרישום. שימו לב שהטיימר התחיל לפעול, כצפוי.
- לוחצים על הלחצן הראשי כדי להעביר את האפליקציה לרקע. שימו לב שהטיימר הפסיק לפעול כצפוי.
מה יקרה לאפליקציה ולנתונים שלה אם Android יכבה את האפליקציה בזמן שהיא פועלת ברקע? חשוב להבין את המקרה המסובך הזה.
כשהאפליקציה עוברת לרקע, היא לא נמחקת, אלא מפסיקה ומחכה שהמשתמש יחזור אליה. אבל אחד הדאגות העיקריות של Android OS הוא לשמור על הפעילות השוטפת בחזית. לדוגמה, אם המשתמש משתמש באפליקציית GPS כדי לעזור לו לאתר אוטובוס, חשוב לעבד את אפליקציית ה-GPS במהירות ולהמשיך להציג את המסלול. פחות חשוב להשאיר את אפליקציית DessertClicker, שייתכן שהמשתמש לא בדק במשך כמה ימים, ברקע.
Android מווסת את אפליקציות הרקע כך שאפליקציה בחזית תוכל לפעול ללא בעיות. לדוגמה, Android מגביל את כמות העיבוד שאפליקציות שפועלות ברקע יכולות לבצע.
לפעמים Android מכבה את כל התהליך של האפליקציה, כולל כל פעילות המשויכת אליו. מערכת Android מבצעת כיבוי מסוג זה כאשר המערכת עמוסה ובסכנה של עיכובים חזותיים, כך שלא יתבצעו קריאה חוזרת או קוד נוסף בשלב זה. התהליך של האפליקציה מושבת, שקט ברקע. עם זאת, נראה שהמשתמש לא נסגר. כשהמשתמש מנווט בחזרה לאפליקציה מערכת ההפעלה של Android נסגר, Android מפעיל מחדש את האפליקציה.
במשימה הזו, אתם מדמים כיבוי של תהליך Android ובודקים מה קורה לאפליקציה כשהיא מופעלת שוב.
שלב 1: משתמשים ב-Adb כדי לדמות סגירת תהליך
ממשק הגישור של Android (adb
) הוא כלי שורת פקודה שמאפשר לך לשלוח הוראות לאמולטורים ולמכשירים המחוברים למחשב. בשלב זה, צריך להשתמש ב-adb
כדי לסגור את תהליך האפליקציה ולראות מה קורה כש-Android משביתה את האפליקציה.
- הידור והפעלה של האפליקציה. לוחצים על קאפקייק כמה פעמים.
- לוחצים על לחצן הבית כדי להעביר את האפליקציה לרקע. האפליקציה שלך מופסקת, והאפליקציה עשויה להיסגר אם מערכת Android תדרוש את המשאבים המשמשים אותה.
- ב-Android Studio, לוחצים על הכרטיסייה Terminal כדי לפתוח את המסוף של שורת הפקודה.
- יש להקליד
adb
וללחוץ על Return.
אם מופיע הרבה פלט שמתחיל ב-Android Debug Bridge version X.XX.X
ומסתיים ב-tags to be used by logcat (see logcat —h
alp), הכול בסדר. אם במקום זאת מופיע הכיתובadb: command not found
, יש לוודא שהפקודהadb
זמינה בנתיב הביצוע. לקבלת הוראות, אפשר לעיין ב "הוספת adb בנתיב הביצוע - אפשר להעתיק את התגובה הזו ולהדביק אותה בשורת הפקודה וללחוץ על Return:
adb shell am kill com.example.android.dessertclicker
פקודה זו מורה לכל המכשירים המחוברים או האמולטורים להפסיק את התהליך עם שם החבילה של dessertclicker
, אבל רק אם האפליקציה פועלת ברקע. מאחר שהאפליקציה שלך פועלת ברקע, לא מוצג שום דבר במכשיר או במסך האמולטור כדי לציין שהתהליך שלך הופסק. ב-Android Studio, לוחצים על הכרטיסייה Run כדי להציג את ההודעה "Applicationפת." לוחצים על הכרטיסייה Logcat כדי לראות שהקריאה החוזרת של onDestroy()
מעולם לא הופעלה — הפעילות שלכם פשוט הסתיימה.
- מסך 'אחרונים' מאפשר לחזור לאפליקציה. האפליקציה מופיעה לאחרונה, גם אם היא הועברה לרקע או הופסקה לגמרי. כשמשתמשים במסך האחרון כדי לחזור לאפליקציה, הפעילות מתחילה מחדש. הפעילות עוברת דרך כל הקבוצה של קריאה חוזרת (callback) למחזור חיים, כולל
onCreate()
. - הערה: כשהאפליקציה מופעלת מחדש, היא מאפסת את "score" (גם את מספר הקינוחים שנמכרו וגם את הסכום הכולל בדולרים) לערכי ברירת המחדל (0). אם Android כבה את האפליקציה, למה היא לא שמרה את המדינה?
אם מערכת ההפעלה תפעיל מחדש את האפליקציה עבורך, מערכת Android תנסה כמיטב יכולתה לאפס את האפליקציה למצב הקודם. Android לוקח את המצב של חלק מהתצוגות ושומר אותו בחבילה כאשר יוצאים מהפעילות. הנה כמה דוגמאות לנתונים שנשמרים באופן אוטומטי: הטקסט שלהם ב-EditText (כל עוד יש להם מזהה בפריסה) וערימה האחורית של הפעילות.
לפעמים, מערכת ההפעלה Android לא יודעת על כל הנתונים שלך. לדוגמה, אם יש לך משתנה מותאם אישית כמוrevenue
באפליקציית DessertClicker, מערכת ההפעלה של Android לא יודעת פרטים על הנתונים האלה או על החשיבות שלהם לפעילות שלך. תצטרכו להוסיף את הנתונים האלה לחבילה בעצמכם.
שלב 2: משתמשים ב-onSaveInstanceState() כדי לשמור נתוני חבילות
השיטה onSaveInstanceState()
היא הקריאה החוזרת שבה אתה משתמש כדי לשמור נתונים שעשויים להיות נחוצים אם מערכת ההפעלה של Android הורסת את האפליקציה שלך. בתרשים הקריאה החוזרת (callback) של מחזור החיים, מתבצעת קריאה אל onSaveInstanceState()
לאחר הפסקת הפעילות. היא נקראת בכל פעם שהאפליקציה שלך עוברת לרקע.
אפשר לחשוב על השיחה ב-onSaveInstanceState()
כאמצעי בטיחות, כך שיש לכם הזדמנות לשמור בחבילה כמות קטנה של מידע, כי הפעילות שלכם יוצאת בחזית. המערכת שומרת את הנתונים האלה עכשיו, כי אם היא ממתינה עד שהם סוגרים את האפליקציה, ייתכן שמערכת ההפעלה תהיה בלחץ משאבים. שמירת הנתונים בכל פעם מבטיחה שנתוני העדכון בחבילה יהיו זמינים לשחזור במקרה הצורך.
- ב-
MainActivity
, אפשר לבטל את הקריאה החוזרת (onSaveInstanceState()
) חזרה ולהוסיף הצהרת יומן שלTimber
.
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
Timber.i("onSaveInstanceState Called")
}
- הידור והפעלה של האפליקציה, ולחיצה על הלחצן דף הבית כדי להעביר אותה לרקע. חשוב לציין שההתקשרות החוזרת של
onSaveInstanceState()
מתבצעת רק אחריonPause()
ו-onStop()
: - בחלק העליון של הקובץ, ממש לפני הגדרת הכיתה, מוסיפים את הקבועים הבאים:
const val KEY_REVENUE = "revenue_key"
const val KEY_DESSERT_SOLD = "dessert_sold_key"
const val KEY_TIMER_SECONDS = "timer_seconds_key"
תשתמשו במפתח הזה כדי לשמור נתונים ולאחזר נתונים מחבילת מצב המופע.
- יש לגלול למטה אל
onSaveInstanceState()
ולראות את הפרמטרoutState
, מסוגBundle
.
חבילה היא אוסף של צמדי מפתח/ערך, שבהם המפתחות הם תמיד מחרוזות. אפשר להוסיף לחבילה ערכים מהדור הישן, כמוint
ו-boolean
.
המערכת הזו שומרת את החבילה הזו ב-RAM, לכן מומלץ לשמור על כמות קטנה של נתונים בחבילה. גם גודל החבילה הזו מוגבל, אבל הגודל משתנה ממכשיר למכשיר. באופן כללי, מומלץ לאחסן הרבה פחות מ-100,000, אחרת עלול לסכן את האפליקציה שלך עם השגיאהTransactionTooLargeException
. - ב-
onSaveInstanceState()
, מזינים את הערךrevenue
(מספר שלם) בחבילה עם השיטהputInt()
:
outState.putInt(KEY_REVENUE, revenue)
השיטה putInt()
(ושיטות דומות מהמחלקה Bundle
כמו putFloat()
ו-putString()
מקבלות שני ארגומנטים: מחרוזת למפתח (קבוע KEY_REVENUE
) והערך בפועל לשמירה.
- חוזרים על אותו תהליך עם מספר הקינוחים שנמכרים ואת הסטטוס של הטיימר:
outState.putInt(KEY_DESSERT_SOLD, dessertsSold)
outState.putInt(KEY_TIMER_SECONDS, dessertTimer.secondsCount)
שלב 3: משתמשים ב-onCreate() כדי לשחזר את נתוני החבילה
- גוללים למעלה עד
onCreate()
ובודקים את החתימה של השיטה:
override fun onCreate(savedInstanceState: Bundle) {
חשוב לשים לב שהערך onCreate()
מקבל Bundle
בכל פעם שמתקשרים אליו. לאחר הפעלה מחדש של הפעילות עקב כיבוי התהליך, החבילה ששמרתם מועברת אל onCreate()
. אם הפעילות מתחילה מחדש, החבילה הזו ב-onCreate()
היא null
. לכן, אם החבילה היא לא null
, זה אומר שאתם &יוצרים מחדש את הפעילות מנקודה קודמת.
- יש להוסיף את הקוד הזה אל
onCreate()
, לאחר הגדרתDessertTimer
:
if (savedInstanceState != null) {
revenue = savedInstanceState.getInt(KEY_REVENUE, 0)
}
הבדיקה של null
קובעת אם יש נתונים בחבילה, או אם החבילה היא null
. פירוש הדבר הוא אם האפליקציה התחילה לפעול מחדש או נוצרה מחדש לאחר כיבוי. הבדיקה הזו היא דפוס נפוץ לשחזור נתונים מהחבילה.
לתשומת ליבך, המפתח שבו השתמשת כאן (KEY_REVENUE
) הוא אותו מפתח שבו השתמשת ל-putInt()
. כדי לוודא שמשתמשים באותו מפתח בכל פעם, מומלץ להגדיר את המפתחות האלה כקבועים. משתמשים ב-getInt()
כדי להוציא את הנתונים מהחבילה, בדיוק כמו שמשתמשים ב-putInt()
כדי להעביר את הנתונים לחבילה. השיטה getInt()
מקבלת שני ארגומנטים:
- מחרוזת המשמשת כמפתח, לדוגמה
"key_revenue"
עבור ערך ההכנסה. - ערך ברירת מחדל במקרה שאין ערך עבור המפתח הזה בחבילה.
המספר השלם שאתם מקבלים מהחבילה מוקצה למשתנה revenue
, וממשק המשתמש ישתמש בערך הזה.
- יש להוסיף שיטות
getInt()
כדי לשחזר את מספר הקינוחים שנמכרו ואת ערך הטיימר:
if (savedInstanceState != null) {
revenue = savedInstanceState.getInt(KEY_REVENUE, 0)dessertsSold = savedInstanceState.getInt(KEY_DESSERT_SOLD, 0)
dessertTimer.secondsCount =
savedInstanceState.getInt(KEY_TIMER_SECONDS, 0)
}
- הידור והפעלה של האפליקציה. יש ללחוץ על קאפקייק לפחות חמש פעמים עד שהוא ישתנה לסופגניות. לוחצים על 'דף הבית' כדי להעביר את האפליקציה לרקע.
- בכרטיסייה Terminal ב-Android Studio, מפעילים את
adb
כדי לסגור את תהליך האפליקציה.
adb shell am kill com.example.android.dessertclicker
- המסך 'אחרונים' מאפשר לחזור לאפליקציה. לתשומת ליבך, בפעם הזו האפליקציה תחזיר את ההכנסה הנכונה ואת ערכי הקינוחים שנמכרו מהחבילה. עם זאת, חשוב לזכור שהקינוח חזר לעוגייה. נותר עוד דבר אחד לעשות כדי לוודא שהאפליקציה תחזור ממצב שבו היא נסגרה.
- ב-
MainActivity
, צריך לבדוק את שיטתshowCurrentDessert()
. לתשומת ליבכם, השיטה הזו קובעת איזו תמונת קינוח תוצג בפעילות על סמך המספר הנוכחי של קינוחים שנמכרים ורשימת הקינוחים במשתנהallDesserts
.
for (dessert in allDesserts) {
if (dessertsSold >= dessert.startProductionAmount) {
newDessert = dessert
}
else break
}
שיטה זו מסתמכת על מספר הקינוחים שנמכרו כדי לבחור את התמונה הנכונה. לכן, לא צריך לעשות דבר כדי לאחסן הפניה לתמונה שבחבילה ב-onSaveInstanceState()
. בחבילה זו אתה כבר מאחסן את מספר הקינוחים שנמכרו.
- בעוד
onCreate()
, בבלוק שמשחזר את המצב מהחבילה, יש להתקשר אלshowCurrentDessert()
:
if (savedInstanceState != null) {
revenue = savedInstanceState.getInt(KEY_REVENUE, 0)
dessertsSold = savedInstanceState.getInt(KEY_DESSERT_SOLD, 0)
dessertTimer.secondsCount =
savedInstanceState.getInt(KEY_TIMER_SECONDS, 0)
showCurrentDessert()
}
- הידור והפעלה של האפליקציה והעברתה לרקע. יש להשתמש ב-
adb
כדי להשבית את התהליך. המסך 'אחרונים' מאפשר לחזור לאפליקציה. לתשומת ליבך, הערכים של הקינוחים שדווחו, ההכנסה הכוללת ותמונת הקינוח משוחזרים כראוי.
יש עוד מקרה מיוחד אחד בניהול הפעילות ומחזור החיים של קטע טקסט שחשוב להבין: איך שינויים בתצורה משפיעים על מחזור החיים של הפעילויות והמקטעים.
שינוי בתצורה קורה כאשר מצב המכשיר משתנה באופן קיצוני, כך שהדרך הקלה ביותר במערכת לפתור את השינוי היא לכבות לחלוטין ולבנות מחדש את הפעילות. לדוגמה, אם המשתמש משנה את שפת המכשיר, ייתכן שהפריסה כולה צריכה להשתנות כדי להתאים למסלול טקסט שונה. אם המשתמש מחבר את המכשיר לאביזר עגינה או מוסיף מקלדת פיזית, ייתכן שפריסת האפליקציה תצטרך להשתמש בגודל תצוגה או בפריסה אחרים. ואם כיוון המכשיר משתנה – אם המכשיר מגולף לאורך או לרוחב, ייתכן שיהיה צורך לשנות את הפריסה כדי שתתאים לכיוון החדש.
שלב 1: הצגה של סבב המכשירים וקריאה חוזרת (callback) של מחזור החיים
- הידור והפעלה של האפליקציה ופותחים את Logcat.
- מסובבים את המכשיר או את האמולטור למצב פריסה לרוחב. ניתן לסובב את האמולטור שמאלה או ימינה באמצעות לחצני הסיבוב, או באמצעות
Control
ומקשי החצים (Command
ומקשי חיצים ב-Mac). - בודקים את הפלט ב-Logcat. סינון של הפלט ב-
MainActivity
.
חשוב לשים לב שכאשר המכשיר או האמולטור מסובבים את המסך, המערכת מתקשרת לכל התקשרות חזרה במחזור החיים כדי להשבית את הפעילות. לאחר מכן, בזמן שהפעילות נוצרת מחדש, המערכת מתקשרת לכל הקריאה החוזרת על מחזור החיים כדי להתחיל את הפעילות. - ב-
MainActivity
, הגיב על כל שיטתonSaveInstanceState()
. - הידור והפעלת האפליקציה שלך שוב. לוחצים כמה פעמים על הקאפקייקס ומסובבים את המכשיר או את האמולטור. הפעם כאשר המכשיר מסובב והפעילות מושבתת ונוצרת מחדש, הפעילות מתחילה עם ערכי ברירת המחדל.
כאשר שינוי הגדרה מתרחש, Android משתמש באותה חבילת מצב מופע שעליה למדת במשימה הקודמת, כדי לשמור ולשחזר את מצב האפליקציה. כמו במקרה של סגירת תהליך, יש להשתמש באפליקציהonSaveInstanceState()
כדי להוסיף לחבילה את נתוני האפליקציה. לאחר מכן יש לשחזר את הנתונים ב-onCreate()
כדי להימנע מאובדן נתונים של מצב פעילות אם המכשיר מסובב. - ב-
MainActivity
, מבטלים את התגובה לשיטהonSaveInstanceState()
, מפעילים את האפליקציה, לוחצים על קאפקייק וסובבים את האפליקציה או המכשיר. שימו לב הפעם שנתוני הקינוחים נשמרים בסבב הפעילויות.
פרויקט של Android Studio: DessertClickerFinal
טיפים בנושא מחזור החיים
- אם מגדירים או מתחילים משהו בקריאה חוזרת (callback) במחזור החיים, צריך להפסיק או להסיר את הדבר הזה בקריאה החוזרת (callback) המתאימה. כשמפסיקים את הפעולה הזאת, מקפידים שלא להמשיך לפעול כשאין בה יותר צורך. לדוגמה, אם הגדרת טיימר ב-
onStart()
, צריך להשהות או לעצור את הטיימר ב-onStop()
. - יש להשתמש ב-
onCreate()
רק כדי להפעיל את החלקים באפליקציה שפועלים פעם אחת, כשהאפליקציה מתחילה בפעם הראשונה. יש להשתמש במדיניותonStart()
כדי להפעיל את החלקים באפליקציה שפועלים כאשר האפליקציה מופעלת, ובכל פעם שהאפליקציה חוזרת לחזית.
ספרייה של מחזור חיים
- אפשר להשתמש בספרייה של מחזור החיים של Android כדי לשנות את בקרת מחזור החיים מהפעילות או מקטע הקוד לרכיב האמיתי, שבו צריך להיות מודעים למחזור החיים.
- בעלים של מחזור חיים הם רכיבים שיש להם מחזורי חיים (ולכן גם מירכאות, "), כולל
Activity
ו-Fragment
. בעלי מחזור החיים מטמיעים את הממשקLifecycleOwner
. - צופים במחזור חיים חוזרים לשים לב למצב הנוכחי של מחזור החיים ולבצע משימות כאשר מחזור החיים משתנה. צופים במחזור חיים מטמיעים את הממשק
LifecycleObserver
. - אובייקטים מסוג
Lifecycle
מכילים את המצבים האלה של מחזור החיים, והם מפעילים אירועים כאשר מחזור החיים משתנה.
כדי ליצור קורס המבוסס על מחזור החיים:
- מטמיעים את הממשק של
LifecycleObserver
בכיתות שצריכות להיות מודעים למחזור החיים. - מפעילים את מחזור הצפייה של מחזור החיים באובייקט מחזור החיים מהפעילות או מקטע הקוד.
- בכיתה של הצופים במחזור החיים, יש להוסיף הערות המבוססות על מחזור החיים של מחזור החיים של מחזור החיים.
לדוגמה,@OnLifecycleEvent(Lifecycle.Event.ON_START)
ההערה מציינת שהשיטה צופה באירועonStart
של מחזור החיים.
עיבוד של פעולות השבתה ושמירה של מצב הפעילות
- מערכת Android מווסתת אפליקציות שפועלות ברקע כדי שניתן יהיה להפעיל את האפליקציה בחזית.
- המשתמש לא יכול לדעת אם המערכת השביתה אפליקציה ברקע. האפליקציה עדיין מופיעה במסך האחרון והיא צריכה להתחיל מחדש באותו מצב שבו המשתמש יצא ממנה.
- ממשק הגישור של Android (
adb
) הוא כלי שורת פקודה שמאפשר לך לשלוח הוראות לאמולטורים ולמכשירים המחוברים למחשב. יש לך אפשרות להשתמש ב-adb
כדי לדמות סגירת תהליך באפליקציה. - כש-Android משביתה את תהליך האפליקציה, לא מתבצעת קריאה לשיטה 'מחזור חיים' של
onDestroy()
. האפליקציה פשוט מפסיקה.
שימור פעילות ומצב שבר
- כשהאפליקציה מופעלת ברקע, מיד אחרי שמתבצעת קריאה ל-
onStop()
, נתוני האפליקציה נשמרים בחבילה. חלק מנתוני האפליקציות, כמו התוכן שלEditText
, נשמרים באופן אוטומטי. - החבילה היא מופע של
Bundle
, שהוא אוסף של מפתחות וערכים. המפתחות הם תמיד מחרוזות. - אפשר להשתמש בהתקשרות החוזרת (
onSaveInstanceState()
) כדי לשמור נתונים אחרים בחבילה שרוצים לשמור, גם אם האפליקציה כובתה באופן אוטומטי. כדי להוסיף נתונים לחבילה, יש להשתמש בשיטות החבילה שמתחילות ב-put
, כמוputInt()
. - אפשר לקבל בחזרה את הנתונים מהחבילה בשיטה
onRestoreInstanceState()
, או נפוץ יותר ב-onCreate()
. לשיטהonCreate()
יש פרמטרsavedInstanceState
שמכיל את החבילה. - אם המשתנה
savedInstanceState
מכילnull
, הפעילות התחילה ללא חבילת מדינה ואין נתוני מדינה שיש לאחזר. - כדי לאחזר נתונים מהחבילה עם מפתח, יש להשתמש בשיטות
Bundle
שמתחילות ב-get
, כמוgetInt()
.
שינויים בתצורה
- שינוי בתצורה קורה כאשר מצב המכשיר משתנה באופן קיצוני, ולכן הדרך הקלה ביותר לפתור את השינוי היא לכבות ולבנות מחדש את הפעילות.
- הדוגמה הנפוצה ביותר לשינוי בתצורה היא כשהמשתמש עובר בין המכשיר לאורך, לרוחב או לאורך. שינוי בהגדרות התצורה עשוי להתרחש גם כששפת המכשיר משתנה או כשמקלדת החומרה מחוברת.
- כשמתרחש שינוי בהגדרה, Android מפעיל את כל הקריאות החוזרות למחזור החיים של כל מחזור הפעילות. לאחר מכן Android מפעיל מחדש את הפעילות מאפס, ומריץ את כל הקריאות החוזרות של מחזור החיים של מחזור החיים.
- כש-Android מכבה אפליקציה בגלל שינוי בהגדרות, היא מפעילה מחדש את הפעילות באמצעות חבילת המדינה שזמינה ל-
onCreate()
. - כמו בעת כיבוי התהליך, שומרים את מצב האפליקציה בחבילה ב-
onSaveInstanceState()
.
קורס אוניברסיטה:
התיעוד של מפתח Android:
- פעילויות (מדריך API)
Activity
(הפניה ל-API)- הסבר על מחזור החיים של הפעילות
- טיפול במחזורי חיים עם רכיבים הקשורים למחזור חיים
LifecycleOwner
Lifecycle
LifecycleObserver
onSaveInstanceState()
- שינויים בתצורת הכינוי
- שמירת מצבי ממשק משתמש
אחר:
- Timber (GitHub)
בקטע הזה מפורטות מטלות שיעורי בית אפשריות לתלמידים שעובדים עם קוד Lab הזה, במסגרת קורס בהדרכת מורה. למורה יש אפשרות לבצע את הפעולות הבאות:
- אם צריך, מקצים שיעורי בית.
- ספרו לתלמידים איך מגישים מטלות בשיעורי בית.
- לתת ציונים למטלות שיעורי הבית.
המורים יכולים להשתמש בהצעות האלה כמה שפחות, ומומלץ להקצות להן כל שיעורי בית שדעתם מתאימה להם.
אם אתם עובדים בעצמכם על שיעור הקוד הזה, אתם מוזמנים להשתמש במטלות שיעורי הבית האלה כדי לבחון את הידע שלכם.
שינוי אפליקציה
פותחים את אפליקציית DiceRoller משיעור 1. (אם אפשר, אפשר להוריד את האפליקציה כאן אם היא לא מותקנת במכשיר). הידור והרצה של האפליקציה, ושימו לב שאם מסובבים את המכשיר, הערך הנוכחי של הקוביות אבד. יש להטמיע את onSaveInstanceState()
כדי לשמור את הערך הזה בחבילה ולשחזר את הערך הזה ב-onCreate()
.
מענה על השאלות האלה
שאלה 1
האפליקציה שלך כוללת סימולציה של פיזיקה שדורשת חישוב מורכב. אחר כך המשתמש מקבל שיחת טלפון. איזה מהתנאים הבאים נכון?
- במהלך שיחת הטלפון, עליך להמשיך לחשב את מיקום האובייקטים בהדמיה הפיזיקה.
- במהלך שיחת הטלפון, אתם צריכים להפסיק לחשב את מיקומי האובייקטים בסימולציה לפיזיקה.
שאלה 2
באיזו שיטה של מחזור חיים ברצונך לבטל כדי להשהות את הסימולציה כאשר האפליקציה לא מופיעה במסך?
onDestroy()
onStop()
onPause()
onSaveInstanceState()
שאלה 3
כדי ליצור ידע מחזורי מחזורי בכיתה באמצעות ספריית מחזור החיים של Android, איזה ממשק צריך להטמיע בכיתה?
Lifecycle
LifecycleOwner
Lifecycle.Event
LifecycleObserver
שאלה 4
באילו נסיבות, שיטת onCreate()
בפעילות שלך מקבלת Bundle
(כלומר, ה-Bundle
אינו null
)? ייתכן שתהיה יותר מתשובה אחת.
- לאחר הפעלה מחדש של המכשיר, הפעילות מתחילה מחדש.
- הפעילות מתחילה מאפס.
- הפעילות תתחדש לאחר החזרה מהרקע.
- המכשיר מופעל מחדש.
מעבר לשיעור הבא:
קישורים למעבדות אחרות של הקוד בקורס הזה זמינים בדף הנחיתה של Android Kotlin Fundamentals Codelabs.