חלק מהגדרות המכשיר יכולות להשתנות בזמן שהאפליקציה פועלת. הדרישות האלה כוללות, בין היתר:
- גודל התצוגה של האפליקציה
- כיוון המסך
- גודל ועובי הגופן
- לוקאל
- מצב כהה לעומת מצב בהיר
- זמינות המקלדת
רוב השינויים האלה בתצורה מתרחשים בעקבות אינטראקציה כלשהי של המשתמש. לדוגמה, סיבוב או קיפול של המכשיר משנים את כמות שטח המסך שזמינה לאפליקציה. באופן דומה, שינוי של הגדרות המכשיר כמו גודל הגופן, השפה או העיצוב המועדף משנה את הערכים שלהם באובייקט 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 האלה ומתי כדאי להשתמש בכל אחד מהם.
הגבלת יצירה מחדש של פעילות
אפשר למנוע יצירה מחדש אוטומטית של פעילות עבור שינויים מסוימים בהגדרות.
שחזור של Activity גורם לשחזור של ממשק המשתמש כולו, ושל כל האובייקטים שנגזרים מ-Activity. יכול להיות שיש לכם סיבות טובות להימנע מכך. לדוגמה, יכול להיות שלא תצטרכו לעדכן משאבים באפליקציה במהלך שינוי הגדרה ספציפי, או שיהיה לכם מגבלת ביצועים. במקרה כזה, אפשר להצהיר שהפעילות מטפלת בשינוי ההגדרה בעצמה ולמנוע מהמערכת להפעיל מחדש את הפעילות.
כדי להשבית את היצירה מחדש של פעילות לשינויים מסוימים בהגדרות, מוסיפים את סוג ההגדרה ל-android:configChanges ברשומה <activity> בקובץ AndroidManifest.xml. הערכים האפשריים מפורטים במסמכי התיעוד של מאפיין android:configChanges.
קוד המניפסט הבא משבית את היצירה מחדש של Activity עבור MyActivity כשמשנים את כיוון המסך ואת הזמינות של המקלדת:
<activity
android:name=".MyActivity"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
android:label="@string/app_name">
חלק מהשינויים בהגדרות תמיד גורמים להפעלה מחדש של הפעילות. אי אפשר להשבית אותן. לדוגמה, אי אפשר להשבית את השינוי הדינמי של הצבעים שהוצג ב-Android 12L (רמת API 32).
תגובה לשינויים בהגדרות במערכת התצוגה
במערכת 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.
שמירת המצב
כשמשתמשים בטכניקה הזו, עדיין צריך לשמור על מצב במהלך מחזור החיים הרגיל של הפעילות. הסיבות לכך הן:
- שינויים שאי אפשר להימנע מהם: שינויים בהגדרות שאי אפשר למנוע יכולים להפעיל מחדש את האפליקציה.
- השבתת תהליך: האפליקציה שלכם צריכה להיות מסוגלת לטפל בהשבתת תהליך שהמערכת יזמה. אם המשתמש יוצא מהאפליקציה והיא עוברת לרקע, יכול להיות שהמערכת תסגור את האפליקציה.
תגובה לשינויים בהגדרות ב-Jetpack פיתוח נייטיב
עם Jetpack פיתוח נייטיב, האפליקציה יכולה להגיב בקלות רבה יותר לשינויים בהגדרות.
עם זאת, אם משביתים את Activity היצירה מחדש לכל שינויי ההגדרות שאפשר לעשות זאת, האפליקציה עדיין צריכה לטפל בשינויי ההגדרות בצורה נכונה.
אובייקט Configuration זמין בהיררכיית ממשק המשתמש של Compose עם LocalConfiguration composition local. בכל פעם שמשתנה, פונקציות הניתנות להגדרה שקוראות מ-LocalConfiguration.current מורכבות מחדש. מידע על אופן הפעולה של נתונים מקומיים של קומפוזיציה זמין במאמר נתונים בהיקף מקומי עם CompositionLocal.
דוגמה
בדוגמה הבאה, רכיב שאפשר להרכיב ממנו ממשק משתמש מציג תאריך בפורמט ספציפי.
הרכיב הקומפוזבילי מגיב לשינויים בהגדרות הלוקאל של המערכת על ידי קריאה ל-ConfigurationCompat.getLocales() עם LocalConfiguration.current.
@Composable
fun DateText(year: Int, dayOfYear: Int) {
val dateTimeFormatter = DateTimeFormatter.ofPattern(
"MMM dd",
ConfigurationCompat.getLocales(LocalConfiguration.current)[0]
)
Text(
dateTimeFormatter.format(LocalDate.ofYearDay(year, dayOfYear))
)
}
כדי למנוע יצירה מחדש של Activity כשמשנים את הלוקאל, צריך להגדיר את Activity שמארח את קוד Compose כך שלא יחולו עליו שינויים בהגדרות הלוקאל. כדי לעשות את זה, צריך להגדיר את android:configChanges ל-locale|layoutDirection.
שינויים בהגדרות: מושגים מרכזיים ושיטות מומלצות
אלה המושגים העיקריים שצריך להכיר כשעובדים על שינויים בהגדרות:
- הגדרות: הגדרות המכשיר קובעות איך ממשק המשתמש מוצג למשתמש, למשל גודל התצוגה של האפליקציה, המקום או העיצוב של המערכת.
- שינויים בהגדרות: שינויים בהגדרות מתבצעים באמצעות אינטראקציה של המשתמש. לדוגמה, המשתמש יכול לשנות את הגדרות המכשיר או את האופן שבו הוא מקיים אינטראקציה פיזית עם המכשיר. אין דרך למנוע שינויים בהגדרות.
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().