קורות חיים מרובות

ב-Android 9 (וגרסאות קודמות), אפליקציות נכנסו למצב PAUSED במקרים הבאים:

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

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

ב-Android 10, כל הפעילויות שאפשר להתמקד בהן בראש הערימות הגלויות נמצאות במצב RESUMED. כך אפשר לשפר את התאימות למצב 'חלונות מרובים' ולמצב MD באפליקציות שמשתמשות ב-onPause() במקום ב-onStop() כדי להפסיק את הרענון של ממשק המשתמש ואת האינטראקציה עם המשתמש. מה זה אומר?

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

איור 1. המשך של כמה פעילויות במכשיר מתקפל

איור 2. המשך של כמה משימות בו-זמנית במצב שולחן עבודה

הפעילויות יכולות להיות במצב PAUSED כשלא ניתן להתמקד בהן או בהסתרה חלקית שלהן, למשל:

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

הגישה הזו מציינת לאפליקציות שפעילות יכולה לקבל קלט ממשתמש רק במצב RESUMED. לפני Android 10, פעילויות היו יכולות לקבל קלט גם במצב PAUSED (לדוגמה, כדאי לנסות לגעת בשתי הפעילויות בו-זמנית במסך מפוצל במכשיר Android 9).

כדי לשמור על האות resumed מהגרסאות הקודמות של Android (ולתקשר מתי אפליקציות צריכות לקבל גישה למשאבים עם גישה בלעדית או למשאבים מסוג singleton), מערכת Android 10 כוללת קריאה חוזרת חדשה:

Activity#onTopResumedActivityChanged(boolean onTop)

כשהיא מופעלת, הקריאה החוזרת הזו מתבצעת בין Activity#onResume() ל-Activity#onPause(). הקריאה החוזרת הזו היא אופציונלית וניתן לדלג עליה, כך שפעילות יכולה לעבור ממצב RESUMED למצב PAUSED בלי להפוך לפעילות העליונה במערכת. לדוגמה, במצב ריבוי חלונות. הקריאה החוזרת היא אופציונלית, ולכן היא לא חלק ממחזור החיים ומומלץ להשתמש בה רק לעיתים רחוקות.

הפעילות הקודמת בראש הרשימה של הפעילויות שהושעו מחדש מקבלת את onTopResumedActivity(false) ומסיימת את הביצוע שלה לפני שהפעילות הבאה ברשימה מקבלת את onTopResumedActivity(true), אלא אם הפעילות הקודמת נמשכת יותר מדי זמן כדי לטפל בקריאה ל-method ומגיעה לזמן הקצוב של 500 אלפיות השנייה.

תאימות

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

מספר פעילויות שהושעו בתהליך אפליקציה אחד

  • בעיה. ב-Android 9 ומטה, רק פעילות אחת במערכת מופעלת מחדש בכל פעם. כל המעברים בין הפעילויות כוללים השהיה של פעילות אחת לפני שממשיכים בפעילות אחרת. אפליקציות ופלטפורמות מסוימות (כמו Flutter או LocalActivityManager של Android) משתמשות בעובדה הזו ומאחסנות את המצב של הפעילות שהמשיכה ב-singletons.
  • פתרון. ב-Android מגרסה 9 ואילך, אם שתי פעילויות מאותו תהליך מופעלות מחדש, המערכת ממשיכה רק את הפעילות שמדורגת גבוה יותר בסדר Z. אפליקציות שמטורגטות ל-Android 10 יכולות לתמוך בהמשך של כמה פעילויות בו-זמנית.

גישה למצלמה בו-זמנית

  • בעיות. הבעיות האלה קיימות גם ב-Android מגרסה 9 ואילך. לדוגמה, פעילות במסך מלא שהופעלה מחדש עשויה לאבד את המיקוד של המצלמה לפעילות מושהית שמופיעה בחלק העליון במצב 'תמונה בתוך תמונה', אבל היא עשויה להיות חשופה יותר ככל שיותר משתמשים יעברו למצבים של ריבוי חלונות וריבוי מסכים.
    • עקב שינויים במצב RESUME, יכול להיות שהאפליקציות יתנתקו מהמצלמה גם במהלך ההמשך. כדי לפתור את הבעיה, האפליקציות צריכות לטפל בניתוק של המצלמה בלי לקרוס. כשהחיבור מנותק, האפליקציות מקבלות קריאה חוזרת (callback) ללא חיבור וכל הקריאות ל-API מתחילות להוציא את השגיאה CameraAccessException.
    • השימוש ב-resizeableActivity=false לא מבטיח גישה בלעדית למצלמה, כי אפשר לפתוח אפליקציות אחרות שמשתמשות במצלמה במסכים אחרים.
  • פתרונות. מפתחים צריכים לכלול לוגיקה למקרה שבו אפליקציה מנותקת מהמצלמה. אם האפליקציה מנותקת מהמצלמה, השעון צריך לבצע קריאה חוזרת לגבי זמינות המצלמה כדי לנסות להתחבר מחדש ולהמשיך להשתמש במצלמה. בנוסף להודעת החזרה (callback) הקיימת CameraManager#AvailabilityCallback#onCameraAvailable(), ב-Android 10 נוספה CameraManager#AvailabilityCallback#onCameraAccessPrioritiesChanged(), שמתאימה למקרה שבו המיקוד (ועדיפות המצלמה) עוברים בין כמה פעילויות שהושעו. מפתחי אפליקציות צריכים להשתמש בשני פונקציות ה-callbacks האלה כדי לקבוע מתי כדאי לנסות לקבל גישה למצלמה.

חזרה להקראה בכמה קטעים

ב-Android 10, המצב של מחזור החיים של הפעילות נקבע לפי הרשאות הגישה וסדר ה-Z. כדי לוודא שהמצב הנכון אחרי עדכוני החשיפה בפעילות הוא המצב המתאים, ולבדוק איזה מצב מחזור חיים רלוונטי, צריך להפעיל את השיטה ActivityRecord#makeActiveIfNeeded() ממיקומים שונים. ב-Android 10, הערך 'פעיל' הוא RESUMED או PAUSED, והוא פועל רק בשני המקרים האלה.

ב-Android 10, המעקב אחרי חזרה לפעילות מתבצע בנפרד בכל סטאק, במקום במיקום יחיד במערכת. הסיבה לכך היא שאפשר לבצע כמה מעברים בין פעילויות בו-זמנית במצבים עם כמה חלונות. למידע נוסף: ActivityStack#mInResumeTopActivity.

קריאה חוזרת (callback) של פעילות שנמשכת

אחרי פעולות שיכולות להוביל לשינוי משמעותי בפעילות (כמו השקת פעילות, המשך או שינוי בסדר Z), תופעל ActivityStackSupervisor#updateTopResumedActivityIfNeeded(). השיטה הזו בודקת אם הפעילות בחלק העליון שחוזרת על עצמה השתנתה, ומבצעת את העדכון במקרה הצורך. אם הפעילות הקודמת בעדיפות הגבוהה ביותר להמשך לא שחררה את המצב בעדיפות הגבוהה ביותר להמשך, נשלחת אליה הודעה על אובדן המצב בעדיפות הגבוהה ביותר להמשך, ומתוזמן זמן קצוב לתפוגה בצד השרת (ActivityStackSupervisor#scheduleTopResumedStateLossTimeout()). דוח של המצב בעדיפות הגבוהה ביותר להמשך נשלח לפעילות הבאה אחרי שהפעילות הקודמת שחררה את המצב, או כשהזמן הקצוב לתפוגה פג (ראו שימושים ב-:

ActivityStackSupervisor#scheduleTopResumedActivityStateIfNeeded()

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

המצב של ההמשך העליון נשמר בצד הלקוח, וכל פעם שהפעילות עוברת ל-RESUMED או ל-PAUSED, היא גם בודקת אם צריך להפעיל את הפונקציה החוזרת onTopResumedActivityChanged(). כך מתאפשרת הפרדה מסוימת בתקשורת בין המצבים של מחזור החיים לבין המצב העדכני ביותר בין השרת לצד הלקוח.