מושגים והטמעה ב-Jetpack פיתוח נייטיב
חלק מהגדרות המכשיר יכולות להשתנות בזמן שהאפליקציה פועלת. הדרישות האלה כוללות, בין היתר:
- גודל התצוגה של האפליקציה
- כיוון המסך
- גודל ועובי הגופן
- לוקאל
- מצב כהה לעומת מצב בהיר
- זמינות המקלדת
רוב השינויים האלה בתצורה מתרחשים בעקבות אינטראקציה כלשהי של המשתמש. לדוגמה, סיבוב או קיפול של המכשיר משנים את כמות שטח המסך שזמינה לאפליקציה. באופן דומה, שינוי של הגדרות המכשיר כמו גודל הגופן, השפה או העיצוב המועדף משנה את הערכים שלהם באובייקט Configuration.
הפרמטרים האלה בדרך כלל דורשים שינויים גדולים מספיק בממשק המשתמש של האפליקציה, ולכן לפלטפורמת Android יש מנגנון ייעודי לשינויים כאלה.
המנגנון הזה נקרא יצירה מחדש של Activity.
יצירת פעילות
המערכת יוצרת מחדש את Activity כשמתרחש שינוי בהגדרות. לשם כך, המערכת קוראת ל-onDestroy ומשמידה את מופע Activity הקיים. לאחר מכן המערכת יוצרת מכונה חדשה באמצעות onCreate, ומכונת Activity החדשה הזו מאותחלת עם ההגדרה החדשה והמעודכנת. המשמעות היא שהמערכת יוצרת מחדש את ממשק המשתמש עם ההגדרה החדשה.
ההתנהגות של יצירה מחדש עוזרת לאפליקציה להסתגל להגדרות חדשות על ידי טעינה מחדש אוטומטית של האפליקציה עם משאבים חלופיים שתואמים להגדרת המכשיר החדשה.
דוגמה לשימוש ב-Recreation
נניח שיש TextView שמציג כותרת סטטית באמצעות android:text="@string/title", כפי שמוגדר בקובץ XML של פריסה. כשיוצרים את התצוגה, הטקסט מוגדר בדיוק פעם אחת, על סמך השפה הנוכחית. אם השפה משתנה, המערכת יוצרת מחדש את הפעילות. כתוצאה מכך, המערכת יוצרת מחדש את התצוגה ומאתחלת אותה לערך הנכון על סמך השפה החדשה.
בנוסף, השחזור מוחק את כל המצב שנשמר כשדות ב-Activity או בכל אחד מהאובייקטים שכלולים בו, כמו Fragment, View או אובייקטים אחרים. הסיבה לכך היא שיצירה מחדש של Activity יוצרת מופע חדש לחלוטין של Activity ושל ממשק המשתמש. בנוסף, Activity הישן כבר לא גלוי או תקף, ולכן כל הפניות שנותרו אליו או לאובייקטים שהוא מכיל הן לא עדכניות. הם עלולים לגרום לבאגים, לדליפות זיכרון ולקריסות.
הציפיות של המשתמשים
המשתמש באפליקציה מצפה שהמצב יישמר. אם משתמש ממלא טופס ופותח אפליקציה אחרת במצב מרובה חלונות כדי לעיין במידע, חוויית המשתמש תהיה גרועה אם הוא יחזור לטופס ריק או למקום אחר באפליקציה. המפתחים צריכים לספק חוויית משתמש עקבית באמצעות שינויים בהגדרות ויצירה מחדש של פעילות.
כדי לוודא שהמצב נשמר באפליקציה, אפשר לבצע פעולות שגורמות לשינויים בהגדרות בזמן שהאפליקציה פועלת בחזית ובזמן שהיא פועלת ברקע. פעולות אלה כוללות:
- סיבוב המכשיר
- כניסה למצב ריבוי חלונות
- שינוי הגודל של האפליקציה במצב ריבוי חלונות או בחלון חופשי
- קיפול מכשיר מתקפל עם כמה מסכים
- שינוי העיצוב של המערכת, כמו מצב כהה לעומת מצב בהיר
- שינוי גודל הגופן
- שינוי השפה של המערכת או של האפליקציה
- חיבור או ניתוק של מקלדת פיזית
- חיבור או ניתוק של תחנת עגינה
יש שלוש גישות עיקריות שבהן אפשר להשתמש כדי לשמור את המצב הרלוונטי במהלך יצירה מחדש של Activity. הסוג שבו משתמשים תלוי בסוג המצב שרוצים לשמור:
- התמדה מקומית כדי לטפל בסיום התהליך בנתונים מורכבים או גדולים.
אחסון מקומי קבוע כולל מסדי נתונים או
DataStore. - אובייקטים שנשמרו כמו מופעים של
ViewModelלטיפול במצב שקשור לממשק המשתמש בזיכרון בזמן שהמשתמש משתמש באפליקציה באופן פעיל. - שמירת מצב המופע כדי לטפל בהשבתת תהליך שהמערכת יזמה ולשמור מצב זמני שתלוי בקלט של משתמשים או בניווט.
במאמר שמירת מצבי ממשק המשתמש מוסבר בפירוט על כל אחד מממשקי ה-API האלה ומתי כדאי להשתמש בכל אחד מהם.
תגובה לשינויים בהגדרות במערכת התצוגה
במערכת View, כשמתרחש שינוי בהגדרות שביטל את היצירה מחדש של Activity, הפעילות מקבלת קריאה ל-Activity.onConfigurationChanged. גם תצוגות שמצורפות מקבלות קריאה ל-View.onConfigurationChanged. לגבי שינויים בהגדרות שלא הוספתם ל-android:configChanges, המערכת יוצרת מחדש את הפעילות כרגיל.
שיטת הקריאה החוזרת onConfigurationChanged מקבלת אובייקט Configuration שמציין את הגדרת המכשיר החדשה. קוראים את השדות באובייקט Configuration כדי להבין מה ההגדרה החדשה. כדי לבצע את השינויים הבאים, מעדכנים את המשאבים שבהם משתמשים בממשק. כשהמערכת מפעילה את השיטה הזו, אובייקט Resources של הפעילות מתעדכן כדי להחזיר משאבים על סמך ההגדרה החדשה. כך תוכלו לאפס רכיבים בממשק המשתמש בלי שהמערכת תפעיל מחדש את הפעילות שלכם.
לדוגמה, ההטמעה הבאה של onConfigurationChanged בודקת אם יש מקלדת:
Kotlin
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
// Checks whether a keyboard is available
if (newConfig.keyboardHidden === Configuration.KEYBOARDHIDDEN_YES) {
Toast.makeText(this, "Keyboard available", Toast.LENGTH_SHORT).show()
} else if (newConfig.keyboardHidden === Configuration.KEYBOARDHIDDEN_NO) {
Toast.makeText(this, "No keyboard", Toast.LENGTH_SHORT).show()
}
}
Java
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Checks whether a keyboard is available
if (newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_YES) {
Toast.makeText(this, "Keyboard available", Toast.LENGTH_SHORT).show();
} else if (newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO){
Toast.makeText(this, "No keyboard", Toast.LENGTH_SHORT).show();
}
}
אם לא צריך לעדכן את האפליקציה בהתאם לשינויים האלה בהגדרות, אפשר במקום זאת לא להטמיע את onConfigurationChanged. במקרה כזה, כל המשאבים שהיו בשימוש לפני שינוי ההגדרות עדיין בשימוש, ורק נמנעתם מהפעלה מחדש של הפעילות. לדוגמה, אפליקציה לטלוויזיה לא צריכה להגיב כשמצרפים או מנתקים מקלדת Bluetooth.
שמירת המצב
כשמשתמשים בטכניקה הזו, עדיין צריך לשמור על מצב במהלך מחזור החיים הרגיל של הפעילות. הסיבות לכך הן:
- שינויים שאי אפשר להימנע מהם: שינויים בהגדרות שאי אפשר למנוע יכולים להפעיל מחדש את האפליקציה.
- השבתת תהליך: האפליקציה שלכם צריכה להיות מסוגלת לטפל בהשבתת תהליך שהמערכת יזמה. אם המשתמש יוצא מהאפליקציה והיא עוברת לרקע, יכול להיות שהמערכת תסגור את האפליקציה.
שינויים בהגדרות: מושגים מרכזיים ושיטות מומלצות
אלה המושגים העיקריים שצריך להכיר כשעובדים על שינויים בהגדרות:
- הגדרות: הגדרות המכשיר קובעות איך ממשק המשתמש מוצג למשתמש, למשל גודל התצוגה של האפליקציה, המקום או העיצוב של המערכת.
- שינויים בהגדרות: שינויים בהגדרות מתבצעים באמצעות אינטראקציה של המשתמש. לדוגמה, המשתמש יכול לשנות את הגדרות המכשיר או את האופן שבו הוא מקיים אינטראקציה פיזית עם המכשיר. אין דרך למנוע שינויים בהגדרות.
- יצירה מחדש של
Activity: שינויים בהגדרות גורמים ליצירה מחדש שלActivityכברירת מחדל. זהו מנגנון מובנה לאתחול מחדש של מצב האפליקציה עבור ההגדרה החדשה. Activityהשמדה:Activityיצירה מחדש גורמת למערכת להשמיד את המופע הישן שלActivityוליצור מופע חדש במקומו. המופע הישן לא רלוונטי יותר. הפניות שנותרו אליו גורמות לדליפות זיכרון, לבאגים או לקריסות.- מצב: המצב במופע הישן
Activityלא קיים במופע החדשActivity, כי אלה שני מופעים שונים של אובייקט. שמירה של מצב האפליקציה והמשתמש, כפי שמתואר במאמר שמירת מצבי ממשק המשתמש. - ביטול הסכמה: ביטול ההסכמה לשחזור פעילות עבור סוג של שינוי בהגדרות הוא אופטימיזציה פוטנציאלית. כדי להשתמש בו, האפליקציה צריכה להתעדכן בצורה תקינה בתגובה להגדרה החדשה.
כדי לספק חוויית משתמש טובה, מומלץ לפעול לפי השיטות המומלצות הבאות:
- היערכות לשינויים תכופים בהגדרות: אל תניחו ששינויים בהגדרות הם נדירים או לא קורים אף פעם, בלי קשר לרמת ה-API, לגורם הצורה או לערכת הכלים של ממשק המשתמש. כשמשתמש גורם לשינוי בהגדרות, הוא מצפה שהאפליקציות יתעדכנו וימשיכו לפעול בצורה תקינה עם ההגדרות החדשות.
- שמירת המצב: לא לאבד את מצב המשתמש כשמתבצעת יצירה מחדש של
Activity. שומרים את המצב כמו שמתואר במאמר שמירת מצבי ממשק המשתמש. - לא כדאי להשתמש בהשבתה כפתרון מהיר: אל תשביתו את
Activityהיצירה מחדש כקיצור דרך כדי למנוע אובדן נתונים. גם אם משביתים את יצירת הפעילות מחדש, עדיין יכול להיות שהמצב ייעלם בגלל יצירה מחדש שלActivityמשינויים אחרים בהגדרות, השבתת תהליך או סגירת האפליקציה. אי אפשר להשבית לגמרי את יצירתActivityמחדש. שומרים את המצב כמו שמתואר במאמר שמירת מצבי ממשק המשתמש. - לא להימנע משינויים בהגדרות: לא להגביל את הכיוון, יחס הגובה-רוחב או שינוי הגודל כדי להימנע משינויים בהגדרות ומ
Activityיצירה מחדש. הדבר פוגע במשתמשים שרוצים להשתמש באפליקציה בדרך המועדפת עליהם.
טיפול בשינויים בהגדרות על סמך גודל
שינויים בהגדרות על סמך גודל יכולים לקרות בכל שלב, והם סבירים יותר אם האפליקציה פועלת במכשיר עם מסך גדול שבו המשתמשים יכולים להיכנס למצב מרובה חלונות. הם מצפים שהאפליקציה תפעל היטב בסביבה הזו.
יש שני סוגים כלליים של שינויים בגודל: משמעותיים ולא משמעותיים. שינוי גודל משמעותי הוא שינוי שבו חל על ההגדרה החדשה סט שונה של משאבים חלופיים בגלל הבדל בגודל המסך, כמו רוחב, גובה או הרוחב הקטן ביותר. המקורות האלה כוללים את אלה שהאפליקציה מגדירה בעצמה ואת אלה מכל אחת מהספריות שלה.
הגבלת יצירה מחדש של פעילות לשינויים בהגדרות שמבוססים על גודל
כשמשביתים את Activity היצירה מחדש לשינויים בהגדרות לפי גודל, המערכת לא יוצרת מחדש את Activity. במקום זאת, הוא מקבל שיחה למספר Activity.onConfigurationChanged. כל התצוגות המצורפות מקבלות קריאה אל View.onConfigurationChanged.
יצירה מחדש של Activity מושבתת לשינויים בהגדרות לפי גודל כשמוסיפים את android:configChanges="screenSize|smallestScreenSize|orientation|screenLayout" לקובץ המניפסט.
התרת יצירה מחדש של פעילות לשינויים בהגדרות שמבוססים על גודל
ב-Android 7.0 (רמת API 24) ומעלה, יצירה מחדש של Activity רק שינויים בהגדרות שמבוססים על גודל מתרחשת אם השינוי בגודל משמעותי. אם המערכת לא יוצרת מחדש Activity בגלל גודל לא מספיק, יכול להיות שהיא תפעיל במקום זאת את Activity.onConfigurationChanged ואת View.onConfigurationChanged.
יש כמה הבהרות לגבי קריאות חוזרות (callback) של Activity ו-View
כשלא נוצר מחדש Activity:
- ב-Android 11 (רמת API 30) עד Android 13 (רמת API 33), לא מתבצעת קריאה ל-
Activity.onConfigurationChanged. - יש בעיה מוכרת שבה יכול להיות שהפונקציה
View.onConfigurationChangedלא תופעל במקרים מסוימים ב-Android 12L (רמת API 32) ובגרסאות מוקדמות של Android 13 (רמת API 33). מידע נוסף זמין בבעיה הציבורית הזו. הבעיה הזו נפתרה בגרסאות מאוחרות יותר של Android 13 וב-Android 14.
אם יש לכם קוד שתלוי בהאזנה לשינויים בהגדרות שמבוססים על גודל, מומלץ להשתמש בכלי View עם View.onConfigurationChanged שהוחלףActivity במקום להסתמך על יצירה מחדש או על Activity.onConfigurationChanged.