חלק מהגדרות המכשיר יכולות להשתנות בזמן שהאפליקציה פועלת. הדוגמאות כוללות, בין היתר:
- גודל התצוגה של האפליקציה
- צפיפות המסך (שינוי קנה המידה של התצוגה)
- כיוון המסך
- גודל ועובי הגופן
- לוקאל
- עיצוב כהה לעומת עיצוב בהיר
- זמינות המקלדת
רוב השינויים האלה בתצורה מתרחשים בעקבות אינטראקציה כלשהי של המשתמש. לדוגמה, סיבוב או קיפול של המכשיר משנים את כמות שטח המסך שזמינה לאפליקציה. חיבור למסכים חיצוניים או העברת אפליקציה בין מסכים עם צפיפות פיקסלים שונה משנים את צפיפות המסך ואת גודל התצוגה. באופן דומה, שינוי הגדרות המכשיר כמו קנה מידה של התצוגה, גודל הגופן, השפה או העיצוב המועדף משנה את הערכים המתאימים באובייקט Configuration.
בדרך כלל, הפרמטרים האלה דורשים שינויים גדולים מספיק בממשק המשתמש של האפליקציה, ולכן לפלטפורמת Android יש מנגנון ייעודי לשינויים כאלה.
המנגנון הזה נקרא יצירה מחדש של Activity.
יצירת פעילות
המערכת יוצרת מחדש Activity כשמתרחש שינוי בהגדרות. כדי לעשות את זה, המערכת קוראת ל-onDestroy ומשמידה את מופע Activity הקיים. לאחר מכן המערכת יוצרת מכונה חדשה באמצעות onCreate, ומכונת Activity החדשה הזו מאותחלת עם ההגדרות החדשות והמעודכנות. המשמעות היא שהמערכת יוצרת מחדש את ממשק המשתמש עם ההגדרה החדשה.
בדרך כלל, Activity משמש כמאחסן של קומפוזיציות. כשיוצרים מחדש את Activity, Compose יוצר מחדש גם את ממשק המשתמש באמצעות ערכי ההגדרה החדשים.
ההתנהגות של יצירה מחדש עוזרת לאפליקציה להסתגל להגדרות חדשות על ידי טעינה מחדש אוטומטית של האפליקציה עם משאבים חלופיים שתואמים להגדרת המכשיר החדשה.
דוגמה לבילוי
נניח שיש קומפוזבילי שמציג כותרת סטטית באמצעות משאב מחרוזת:
// In the res/values/strings.xml file // <string name="compose">Jetpack Compose</string> // In your Compose code Text( text = stringResource(R.string.compose) )
כשיוצרים את הרכיב Activity, הרכיב הקומפוזבילי Text קורא את ההגדרה הנוכחית (למשל, השפה) ומחזיר את משאב המחרוזת המתאים.
אם השפה משתנה, המערכת יוצרת מחדש את הפעילות. במקרה כזה, ממשק המשתמש של 'כתיבה' נוצר מחדש. מכיוון שהפונקציה stringResource קוראת מההגדרה הנוכחית, שם הפריט מתעדכן אוטומטית לערך המקומי הנכון.
בנוסף, השחזור מנקה את כל המצב שנשמר כשדות ב-Activity.
כדי לשמור על מצב ממשק המשתמש כשמבצעים שינויים בהגדרות, צריך להשתמש בדפוסי ניהול מצב מומלצים. משתמשים ב-ViewModel לנתונים וללוגיקה עסקית, וב-rememberSaveable למצב ברמת ממשק המשתמש. באמצעות המנגנונים האלה, המצב שלכם נשמר כשמבצעים יצירה מחדש של Activity, והממשק מתעדכן בהתאם להגדרה החדשה.
מידע נוסף על שמירת מצב ב-Compose זמין במאמר שמירת מצב ממשק המשתמש ב-Compose.
הציפיות של המשתמשים
המשתמש באפליקציה מצפה שהמצב יישמר. אם משתמש ממלא טופס ופותח אפליקציה אחרת במצב מרובה חלונות כדי לעיין במידע, חוויית המשתמש תהיה גרועה אם הוא יחזור לטופס ריק או למקום אחר באפליקציה. כמפתחים, אתם צריכים לספק חוויית משתמש עקבית באמצעות שינויים בהגדרות ויצירה מחדש של פעילות.
כדי לוודא שהמצב נשמר באפליקציה, אפשר לבצע פעולות שגורמות לשינויים בהגדרות בזמן שהאפליקציה פועלת בחזית ובזמן שהיא פועלת ברקע. פעולות אלה כוללות:
- סיבוב המכשיר
- כניסה למצב ריבוי חלונות
- שינוי הגודל של האפליקציה במצב ריבוי חלונות או בחלון חופשי
- קיפול של מכשיר מתקפל עם כמה מסכים
- שינוי גודל התצוגה או קנה המידה של התצוגה בהגדרות המערכת
- העברת האפליקציה בין מסכים עם צפיפות מסך שונה (כמו מסכים חיצוניים מחוברים או המסכים של מכשיר מתקפל)
- שינוי העיצוב של המערכת, למשל מעיצוב כהה לעיצוב בהיר
- שינוי גודל הגופן
- שינוי השפה של המערכת או של האפליקציה
- חיבור או ניתוק של מקלדת פיזית
- חיבור או ניתוק של תחנת עגינה
יש כמה גישות שאפשר לנקוט כדי לשמור על מצב רלוונטי באמצעות יצירה מחדש של Activity. הסוג שבו משתמשים תלוי בסוג המצב שרוצים לשמור:
- התמדה מקומית כדי לטפל בסיום התהליך בנתונים מורכבים או גדולים.
אחסון מקומי קבוע כולל מסדי נתונים או
DataStore. - אובייקטים שנשמרו כמו מופעים של
ViewModelכדי לטפל במצב שקשור לממשק המשתמש בזיכרון בזמן שהמשתמש משתמש באפליקציה באופן פעיל. -
rememberSaveableכדי לשמור את מצב ממשק המשתמש הזמני במהלך שינויים בהגדרות וסיום תהליך שהמערכת יזמה. האפשרות הזו מתאימה למצב שתלוי בקלט של המשתמש, במיקום הגלילה או בניווט, אבל לא שייך ל-ViewModel.
במאמר שמירת מצבי ממשק המשתמש מוסבר בפירוט על כל אחד מממשקי ה-API האלה ומתי מתאים להשתמש בכל אחד מהם.
הגבלת יצירה מחדש של פעילות
אפשר למנוע יצירה מחדש אוטומטית של פעילות עבור שינויים מסוימים בהגדרות. באפליקציות מודרניות שמבוססות על Compose בלבד, ממשק המשתמש מורכב מחדש בכל מקרה, אבל מומלץ לטפל בשינוי ההגדרה ישירות.
כברירת מחדל, שינוי בהגדרה גורם למערכת להרוס את הפעילות וליצור אותה מחדש, כולל ממשק המשתמש וכל האובייקטים שנגזרים מהפעילות. אם תצהירו שהפעילות מטפלת בשינוי ההגדרה בעצמה, המערכת תמנע זאת. במקום זאת, רק האובייקט Configuration מתעדכן, ו-Compose מרכיב מחדש את ממשק המשתמש עם הערכים החדשים.
יש כמה יתרונות לטיפול בשינויים בהגדרות ישירות ב-Compose:
- שיפור הביצועים: הרכבה מחדש של ממשק המשתמש זולה יותר ממחזור מלא של יצירת פעילות מחדש, במיוחד כשמדובר בשינויים קלים.
- אנימציות חלקות: הימנעות מהפעלה מחדש של 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 17
החל מ-Android 17 (רמת API 37), המערכת לא מפעילה מחדש פעילויות כברירת מחדל עבור כמה שינויים בהגדרות, שבדרך כלל לא דורשים יצירה מחדש מלאה של ממשק המשתמש.
במקום זאת, הפעילויות נשארות פעילות ומקבלות עדכונים באמצעות הקריאה החוזרת onConfigurationChanged().
השינויים הספציפיים בהגדרות כוללים:
CONFIG_KEYBOARDCONFIG_KEYBOARD_HIDDENCONFIG_NAVIGATIONCONFIG_TOUCHSCREENCONFIG_COLOR_MODE-
CONFIG_UI_MODE(רק כשמצב ממשק המשתמש משתנה לUI_MODE_TYPE_DESKאו מUI_MODE_TYPE_DESKלסוג אחר)
אם האפליקציה שלכם מסתמכת על הפעלה מחדש מלאה כדי לטעון מחדש משאבים עבור השינויים האלה, עכשיו אתם צריכים להביע הסכמה מפורשת להתנהגות הישנה באמצעות המאפיין android:recreateOnConfigChanges בקובץ המניפסט.
המאפיין מאפשר לציין אילו שינויים בהגדרות צריכים להפעיל מחזור של עצירה, השמדה ויצירה מחדש של פעילות מלאה, למשל:
<activity
android:name=".MyActivity"
android:recreateOnConfigChanges="keyboard|keyboardHidden|navigation|colorMode|touchscreen|...">
...
</activity>
תגובה לשינויים בהגדרות
עם Jetpack Compose, האפליקציה מגיבה בקלות רבה יותר לשינויים בהגדרות.
עם זאת, אם משביתים את היצירה מחדש של 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.
שינויים בהגדרות: מושגים מרכזיים ושיטות מומלצות
אלה מושגי המפתח שחשוב להכיר כשעובדים על שינויים בהגדרות:
- הגדרות: הגדרות המכשיר קובעות איך ממשק המשתמש מוצג למשתמש, למשל גודל התצוגה של האפליקציה, צפיפות המסך, המקום או העיצוב של המערכת.
ב-Compose, אפשר לגשת לערכי ההגדרה באמצעות
LocalConfiguration. - שינויים בהגדרות: ההגדרות משתנות באמצעות אינטראקציה של המשתמש. לדוגמה, המשתמש יכול לשנות את הגדרות המכשיר או את האופן שבו הוא מתקשר פיזית עם המכשיר. אין דרך למנוע שינויים בהגדרות.
- יצירה מחדש של
Activity: שינויים בהגדרות גורמים ליצירה מחדש שלActivityכברירת מחדל. זהו מנגנון מובנה לאתחול מחדש של מצב האפליקציה עבור ההגדרה החדשה. Activityהשמדה:Activityיצירה מחדש גורמת למערכת להשמיד את המופע הישן שלActivityוליצור מופע חדש במקומו. המופע הישן לא רלוונטי יותר. מומלץ להימנע משמירת הפניות לאובייקטים עם היקף מחזור חיים מעבר להיקף המיועד שלהם.- מצב: המצב במופע הישן של
Activityלא קיים במופע החדש שלActivity, כי אלה שני מופעים שונים של אובייקטים. במקום לקשר את המצב לפעילות, צריך להשתמש בממשקי API מומלצים כדי לשמור את מצב האפליקציה והמשתמש, כמו שמתואר במאמר שמירת מצבי ממשק המשתמש. - ביטול הסכמה: כדי לבטל את ההסכמה ליצירה מחדש של פעילות עבור סוג של שינוי בהגדרות, צריך לוודא שהאפליקציה מתעדכנת בצורה תקינה בתגובה להגדרה החדשה.
כדי לספק חוויית משתמש טובה, מומלץ לפעול לפי השיטות המומלצות הבאות:
- היערכות לשינויים תכופים בהגדרות: אל תניחו ששינויים בהגדרות הם נדירים או לא קורים אף פעם, בלי קשר לרמת ה-API, לגורם הצורה או לערכת הכלים של ממשק המשתמש. כשמשתמש גורם לשינוי בהגדרות, הוא מצפה שהאפליקציות יתעדכנו וימשיכו לפעול בצורה תקינה עם ההגדרות החדשות.
- שמירת המצב: לא לאבד את מצב המשתמש כשמתרחש
Activityיצירה מחדש של האפליקציה. שומרים את המצב כמו שמתואר במאמר שמירת מצבי ממשק המשתמש באמצעות ממשקי API כמוViewModelו-rememberSaveable. - לא כדאי להשתמש בהשבתה כפתרון מהיר: אל תשביתו את
Activityהיצירה מחדש כקיצור דרך כדי למנוע אובדן נתונים. כדי להשבית את יצירת הפעילות מחדש, צריך לטפל בשינוי, ועדיין יכול להיות שהמצב ייעלם בגלל יצירה מחדש שלActivityמשינויים אחרים בהגדרות, מסגירת התהליך או מסגירת האפליקציה. אי אפשר להשבית לגמרי את יצירתActivityמחדש. שומרים את המצב כמו שמתואר במאמר שמירת מצבי ממשק המשתמש. - לא להימנע משינויים בהגדרות: לא להגביל את הכיוון, יחס הגובה-רוחב או שינוי הגודל כדי להימנע משינויים בהגדרות ומ
Activityיצירה מחדש. הדבר פוגע במשתמשים שרוצים להשתמש באפליקציה שלכם בדרך המועדפת עליהם.
טיפול בשינויים בהגדרות על סמך גודל
שינויים בהגדרות על סמך גודל יכולים לקרות בכל שלב, והם סבירים יותר כשהאפליקציה פועלת במכשיר עם מסך גדול שבו המשתמשים יכולים להיכנס למצב מרובה חלונות. הם מצפים שהאפליקציה תפעל בצורה טובה בסביבה הזו.
יש שני סוגים כלליים של שינויים בגודל: משמעותיים ולא משמעותיים. שינוי גודל משמעותי הוא שינוי שבו חל על ההגדרה החדשה סט שונה של משאבים חלופיים, בגלל הבדל בגודל המסך, כמו רוחב, גובה או הרוחב הקטן ביותר. המקורות האלה כוללים את המקורות שהאפליקציה מגדירה בעצמה ואת המקורות מכל אחת מהספריות שלה.
הגבלת יצירה מחדש של פעילות לשינויים בהגדרות שמבוססים על גודל
כשמשביתים את האפשרות Activity יצירה מחדש לשינויים בהגדרות שמבוססים על גודל, המערכת לא יוצרת מחדש את Activity. במקום זאת, הוא מקבל שיחה למספר Activity.onConfigurationChanged. כל רכיבי ה-Composable שקוראים את LocalConfiguration.current יורכבו מחדש באופן אוטומטי כדי לשקף את הגודל החדש.
היצירה מחדש של Activity מושבתת לשינויים בהגדרות שמבוססים על גודל, אם יש לכם android:configChanges="screenSize|smallestScreenSize|orientation|screenLayout" בקובץ המניפסט.
מקורות מידע נוספים
למידע נוסף על טיפול בשינויים בהגדרות, אפשר לעיין במקורות המידע הנוספים הבאים: