שמירת המצב עם מקטעים

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

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

  • משתנים: משתנים מקומיים בחלק.
  • מצב התצוגה: כל נתון ששייך לתצוגה מפורטת אחת או יותר בקטע.
  • SavedState: נתונים ייחודיים למופעי הפלח הזה שצריך לשמור ב-onSaveInstanceState().
  • NonConfig: נתונים שנשלפו ממקור חיצוני, כמו שרת או מאגר מקומי, או נתונים שנוצרו על ידי משתמשים ונשלחים לשרת לאחר ביצוע ההתחייבות.

לרוב, המערכת מתייחסת ל-Variables כמו ל-SavedState, אבל בטבלה הבאה מוסבר ההבדל בין השניים כדי להמחיש את ההשפעה של הפעולות השונות על כל אחד מהם.

פעולה משתנים הצגת המצב SavedState NonConfig
הוספה לערימה הקודמת x
שינוי בתצורה x
תהליך מוות/פנאי x ✓*
הוסר ולא נוסף לערימה הקודמת x x x x
המארח הסתיים x x x x

* אפשר לשמור על מצב NonConfig בכל מוות של תהליך באמצעות מודול המצב השמור ל-ViewModel.

טבלה 1: פעולות הרסניות שונות של מקטעים וההשפעות שיש להן על סוגי מצבים שונים.

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

אפליקציה ליצירת טקסט אקראי שממחישה סוגים שונים של מצב
איור 1. אפליקציה ליצירת טקסט אקראי שממחישה סוגים שונים של מצב.

בדוגמה הזו, נניח שכאשר המשתמש לוחץ על לחצן העריכה, באפליקציה מוצגת תצוגה מסוג EditText שבה המשתמש יכול לערוך את ההודעה. אם המשתמש ילחץ על ביטול, התצוגה EditText אמורה להימחק ולהגדיר את החשיפה ל-View.GONE. כדי להציג מסך כזה, יכול להיות שתצטרכו לנהל ארבעה פריטים של נתונים כדי להבטיח חוויה חלקה:

נתונים סוג סוג המדינה תיאור
seed Long NonConfig זרע שמשמש ליצירה אקראית של מעשה טוב חדש. נוצר כשה-ViewModel נוצר.
randomGoodDeed String SavedState + משתנה נוצר כשהקטע נוצר בפעם הראשונה. randomGoodDeed נשמר כדי להבטיח שהמשתמשים יראו את אותו מעשה טוב אקראי גם אחרי מוות אקראי וחידוש.
isEditing Boolean SavedState + משתנה דגל בוליאני שמוגדר כ-true כשהמשתמש מתחיל לערוך. isEditing נשמר כדי לוודא שחלק המסך שמשמש לעריכה יישאר גלוי כשהקטע ייווצר מחדש.
טקסט ערוך Editable הצגת המצב (בבעלות EditText) הטקסט הערוך בתצוגה EditText. התצוגה EditText שומרת את הטקסט הזה כדי לוודא שהשינויים שבוצעו על ידי המשתמש לא ייאבדו.

טבלה 2: מצבים שאפליקציית היצירה של טקסט אקראי חייבת לנהל.

בקטעים הבאים מוסבר איך לנהל את מצב הנתונים בצורה נכונה באמצעות פעולות הרסניות.

הצגת המצב

התצוגות אחראיות לניהול המצב שלהן. לדוגמה, כשתצוגה מקבלת קלט ממשתמש, היא אחראית לשמור ולשחזר את הקלט הזה כדי לטפל בשינויים בהגדרות. לכל התצוגות הזמינות של framework של Android יש הטמעה משלה של onSaveInstanceState() ו-onRestoreInstanceState(), כך שלא צריך לנהל את מצב התצוגה בתוך המקטע.

לדוגמה, בתרחיש הקודם, המחרוזת הערוכה נשמרת ב-EditText. ל-EditText יש גישה לערך של הטקסט שהוא מציג, וגם לפרטים נוספים, כמו תחילת הטקסט שנבחר וסוף הטקסט שנבחר.

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

<EditText
    android:id="@+id/good_deed_edit_text"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

כפי שצוין בטבלה 1, תצוגות שומרות ומשחזרות את ה-ViewState שלהן באמצעות כל הפעולות שלא מסירות את המקטע או משמידים את המארח.

SavedState

הפלח אחראי לניהול כמויות קטנות של מצב דינמי שמהווה חלק בלתי נפרד מהאופן שבו הפלח פועל. אפשר לשמור נתונים שקל לסדר בסדרה באמצעות Fragment.onSaveInstanceState(Bundle). בדומה ל-Activity.onSaveInstanceState(Bundle), הנתונים שמוסיפים לחבילה נשמרים במהלך שינויי תצורה, מוות של תהליך ויצירה מחדש של תהליך, והם זמינים בשיטות onCreate(Bundle),‏ onCreateView(LayoutInflater, ViewGroup, Bundle) ו-onViewCreated(View, Bundle) של הקטע.

בהמשך לדוגמה הקודמת, randomGoodDeed הוא המשטר שמוצג למשתמש, ו-isEditing הוא דגל שקובע אם הקטע מציג או מסתיר את EditText. צריך לשמור את המצב השמור באמצעות onSaveInstanceState(Bundle), כפי שמתואר בדוגמה הבאה:

Kotlin

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    outState.putBoolean(IS_EDITING_KEY, isEditing)
    outState.putString(RANDOM_GOOD_DEED_KEY, randomGoodDeed)
}

Java

@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putBoolean(IS_EDITING_KEY, isEditing);
    outState.putString(RANDOM_GOOD_DEED_KEY, randomGoodDeed);
}

כדי לשחזר את המצב ב-onCreate(Bundle), מאחזרים את הערך המאוחסן מהחבילה:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    isEditing = savedInstanceState?.getBoolean(IS_EDITING_KEY, false)
    randomGoodDeed = savedInstanceState?.getString(RANDOM_GOOD_DEED_KEY)
            ?: viewModel.generateRandomGoodDeed()
}

Java

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (savedInstanceState != null) {
        isEditing = savedInstanceState.getBoolean(IS_EDITING_KEY, false);
        randomGoodDeed = savedInstanceState.getString(RANDOM_GOOD_DEED_KEY);
    } else {
        randomGoodDeed = viewModel.generateRandomGoodDeed();
    }
}

כמו שצוין בטבלה 1, חשוב לזכור שהמשתנים נשמרים כשמציבים את המקטע בסטאק העורפי. וההתייחסות אליהם כמצב שמור, מבטיחה שהם ימשיכו להתקיים בכל הפעולות ההרסניות.

NonConfig

צריך למקם נתונים שאינם נתוני תצורה מחוץ לפרמנט, למשל ב-ViewModel. בדוגמה הקודמת שלמעלה, הערך seed (המצב NonConfig) נוצר ב-ViewModel. הלוגיקה לשמירה על המצב שלו היא בבעלות ViewModel.

Kotlin

public class RandomGoodDeedViewModel : ViewModel() {
    private val seed = ... // Generate the seed

    private fun generateRandomGoodDeed(): String {
        val goodDeed = ... // Generate a random good deed using the seed
        return goodDeed
    }
}

Java

public class RandomGoodDeedViewModel extends ViewModel {
    private Long seed = ... // Generate the seed

    private String generateRandomGoodDeed() {
        String goodDeed = ... // Generate a random good deed using the seed
        return goodDeed;
    }
}

הכיתה ViewModel מאפשרת באופן מהותי לשמור נתונים אחרי שינויים בהגדרות, כמו סיבוב המסך, והם נשארים בזיכרון כשהקטע מועבר ל-back stack. אחרי מותו של התהליך והיצירה מחדש שלו, ה-ViewModel נוצר מחדש ו-seed חדש נוצר. הוספת מודול SavedState ל-ViewModel מאפשרת ל-ViewModel לשמור מצב פשוט במהלך מוות ויצירה מחדש של התהליך.

מקורות מידע נוספים

למידע נוסף על ניהול מצב של קטעי קוד, תוכלו לעיין במקורות המידע הנוספים הבאים.

שיעורי Lab

מדריכים