טיפול בשינויים בהגדרות

חלק מהגדרות המכשיר עשויות להשתנות בזמן שהאפליקציה פועלת. למשל, אך לא רק:

  • גודל התצוגה של האפליקציה
  • כיוון המסך
  • גודל ומשקל הגופן
  • שפה ואזור
  • מצב כהה לעומת מצב בהיר
  • זמינות המקלדת

רוב השינויים האלה בהגדרות האישיות מתרחשים בגלל אינטראקציות מסוימות של המשתמשים. עבור לדוגמה, סיבוב או קיפול של המכשיר משנים את שטח המסך זמינים לאפליקציה. באותו אופן, כשמשנים את הגדרות המכשיר כמו גודל הגופן או העיצוב המועדף ישנו את הערכים המתאימים אובייקט 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, כשמתרחש שינוי תצורה שעבורו יש לך בילוי של 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 Compose, האפליקציה תוכל להגיב בקלות רבה יותר לשינויים בהגדרות. עם זאת, אם משביתים יצירה מחדש של Activity לכל שינויי ההגדרות שבהם הוא נמצא כדי לעשות זאת, האפליקציה עדיין צריכה לטפל כראוי שינויים בתצורה.

האובייקט Configuration זמין בהיררכיה של ממשק המשתמש של הכתיבה את היצירה המקומית LocalConfiguration. בכל פעם שהוא משתנה, פונקציות קומפוזביליות שקוראות מ-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 מארח את כדי לכתוב את הקוד צריך לבטל את ההסכמה לשינויים בהגדרות הלוקאל. כדי לעשות את זה, להגדיר את 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() במקום זאת.

יש כמה נקודות שכדאי לשים לב אליהן לגבי Activity ו-View קריאות חוזרות (callback) ללא יצירה מחדש של 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()