חלק מההגדרות של המכשיר עשויות להשתנות בזמן שהאפליקציה פועלת. בין היתר, אסור:
- גודל התצוגה של האפליקציה
- כיוון המסך
- גודל הגופן ועובי הגופן
- שפה ואזור
- מצב כהה לעומת מצב בהיר
- זמינות המקלדת
רוב השינויים בתצורה האלה מתרחשים כתוצאה מאינטראקציה כלשהי של משתמש. לדוגמה, סיבוב או קיפול המכשיר משנים את כמות שטח המסך שזמין לאפליקציה. באופן דומה, שינוי הגדרות המכשיר כמו גודל הגופן, השפה או העיצוב המועדף משנים את הערכים המתאימים באובייקט Configuration
.
בדרך כלל, השינויים בפרמטרים האלה דורשים שינויים גדולים מספיק בממשק המשתמש של האפליקציה, ולכן לפלטפורמת Android יש מנגנון ייעודי למקרה שהם משתנים.
המנגנון הזה הוא Activity
יצירה מחדש.
שחזור פעילות
המערכת יוצרת מחדש את Activity
כשמתרחש שינוי בתצורה. לשם כך, המערכת קולטת את onDestroy()
ומשמידה את המכונה הקיימת של Activity
. לאחר מכן נוצרת מכונה חדשה באמצעות onCreate()
, ומכונה ה-Activity
החדשה הזו מאותחלת עם ההגדרות החדשות והמעודכנות. המשמעות היא גם שהמערכת יוצרת מחדש את ממשק המשתמש עם התצורה החדשה.
התנהגות היצירה מחדש עוזרת לאפליקציה להתאים את עצמה להגדרות חדשות על ידי טעינת האפליקציה מחדש באופן אוטומטי עם משאבים חלופיים שתואמים להגדרות המכשיר החדשות.
דוגמה לפעילות פנאי
נניח שיש 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
במערכת View
, כשמתרחש שינוי בהגדרות שלגביו השבתתם את היצירה מחדש של Activity
, הפעילות מקבלת קריאה ל-Activity.onConfigurationChanged()
. גם לתצוגות המצורפות מתבצעת קריאה ל-View.onConfigurationChanged()
. לגבי שינויים בהגדרות שלא הוספתם ל-android:configChanges
, המערכת תיצור מחדש את הפעילות כרגיל.
שיטת ה-callback 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 Compose מאפשר לאפליקציה להגיב בקלות רבה יותר לשינויים בתצורה.
עם זאת, אם משביתים את היצירה מחדש של Activity
לכל שינויי התצורה שבהם אפשר לעשות זאת, האפליקציה עדיין צריכה לטפל בשינויי התצורה בצורה נכונה.
האובייקט Configuration
זמין בהיררכיה של ממשק המשתמש של Compose עם הרכיב המקומי LocalConfiguration
. בכל פעם שהוא משתנה, פונקציות מורכבות שקוראות מ-LocalConfiguration.current
עוברות עיבוד מחדש. למידע נוסף על אופן הפעולה של משתני CompositionLocal, ראו נתונים ברמת המיקום המקומי באמצעות 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
recreation: שינויים בהגדרות יגרמו ליצירה מחדש שלActivity
כברירת מחדל. זהו מנגנון מובנה לאתחול מחדש של מצב האפליקציה בהתאם לתצורה החדשה.Activity
destruction: יצירת מחדש של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()
.
יש כמה דברים שחשוב לזכור לגבי קריאות החזרה (callbacks) של 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()
.