זמן ההפעלה של האפליקציה

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

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

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

הפעלת האפליקציה יכולה להתרחש באחד משלושה מצבים: הפעלה במצב התחלתי (cold start), הפעלה במצב ביניים (warm start) או הפעלה חמה הפעלה. כל מצב משפיע על משך הזמן שעובר עד שהאפליקציה תהיה גלויה למשתמש. בהפעלה במצב התחלתי (cold start), האפליקציה מתחילה מאפס. במדינות האחרות, המערכת צריכה להעביר את האפליקציה שפועלת מהרקע לחזית.

מומלץ תמיד לבצע אופטימיזציה על סמך הנחה של הפעלה במצב התחלתי (cold start). הפעולה הזו יכולה לשפר גם את הביצועים של הפעלות במצב ביניים (warm start) וההפעלה מתוך הזיכרון (hot start).

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

שני מדדים חשובים לקביעת הפעלה של האפליקציה הם הזמן להצגה הראשונית של האפליקציה (TTID) וזמן לשרטוט מלא (TTFD). הנתון הזה הוא הזמן שלוקח להציג את הפריים הראשון, ו-TTFD הוא הזמן שעובר עד שהאפליקציה הופכת אינטראקטיבית מלאה. שני הערכים חשובים באותה מידה, כי בעזרת ה-TTDID המשתמש יכול לדעת שהאפליקציה נטענת, ו-TTDFD הוא הערך שבו אפשר להשתמש באפליקציה בפועל. אם אחד מהם מהם ארוכים מדי, המשתמש עלול לצאת מהאפליקציה לפני שהיא אפילו נטענת במלואה.

הפעלה במצב התחלתי (cold start)

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

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

בתחילת ההפעלה במצב התחלתי (cold start), המערכת מבצעת את שלוש המשימות הבאות:

  1. לטעון ולהפעיל את האפליקציה.
  2. להציג חלון התחלה ריק לאפליקציה מיד לאחר ההפעלה.
  3. יוצרים את תהליך האפליקציה.

ברגע שהמערכת יוצרת את תהליך האפליקציה, תהליך האפליקציה הוא אחראי לשלבים הבאים:

  1. יוצרים את אובייקט האפליקציה.
  2. הפעלת ה-thread הראשי.
  3. יוצרים את הפעילות הראשית.
  4. מתנפח תצוגות.
  5. פריסת המסך.
  6. מבצעים את השרטוט הראשוני.

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

איור 1 מראה איך המערכת והאפליקציה מעבדות את העבודה בין הצדדים אחר.

איור 1. ייצוג חזותי של החלקים החשובים בהפעלה של אפליקציה במצב התחלתי (cold start).

בעיות בביצועים עלולות להתעורר במהלך יצירת האפליקציה והיצירה של פעילות.

יצירת אפליקציה

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

אם תשנו את הערך של המאפיין Application.onCreate() באפליקציה שלכם, המערכת מפעילה את ה-method onCreate() באובייקט של האפליקציה. לאחר מכן האפליקציה מתחילה לפעול ה-thread הראשי, שנקרא גם UIthread, כדי ליצור משימות בפעילות העיקרית.

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

יצירת פעילות

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

  1. אתחול הערכים.
  2. Calls constructors.
  3. קריאה לשיטת הקריאה החוזרת, למשל Activity.onCreate(), מתאימה למצב מחזור החיים הנוכחי של הפעילות.

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

הפעלה במצב ביניים

הפעלה במצב ביניים (warm start) כוללת קבוצת משנה של הפעולות המתרחשות במהלך הפעלה במצב התחלתי (cold start). במקביל, הוא מייצג יותר תקורה מאשר הפעלה מתוך הזיכרון (Hot start). יש הרבה מצבים אפשריים שיכולים להיחשב כהפעלה במצב ביניים (warm start), כמו הבאים:

  • המשתמש יוצא מהאפליקציה אבל מפעיל אותה מחדש. התהליך עשוי ימשיכו לפעול, אבל האפליקציה חייבת ליצור מחדש את הפעילות מאפס באמצעות קריאה אל onCreate().

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

הפעלה מתוך הזיכרון (hot start)

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

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

ההפעלה מתוך הזיכרון (hot start) מוצגת במסך באותו אופן כמו בתרחיש הפעלה במצב התחלתי (cold start). מציג מסך ריק עד שהאפליקציה מסיימת לעבד פעילות.

איור 2. תרשים עם מצבי ההפעלה השונים והתהליכים התואמים שלהם, כל מצב החל מהפריים הראשון מצויר.

איך מזהים את ההפעלה של האפליקציה ב-Perfetto

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

  1. ב-Perfetto, מחפשים את השורה שבה מופיע המדד הנגזר מ-Android App Startups. אם המיקום לא מופיע שם, נסו לתעד מעקב באמצעות מעקב המערכת במכשיר app.

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

  3. כדי להצמיד את השורה 'סטארט-אפים של אפליקציות ל-Android', לוחצים על סמל הסיכה, מופיע כשמחזיקים את הסמן מעל לשורה.

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

  5. כדי להגדיל את השרשור הראשי, בדרך כלל בחלק העליון, לוחצים על w (מקישים על s, a, d כדי להקטין את התצוגה, לזוז שמאלה ולזוז ימינה, בהתאמה).

    איור 4.פרוסת המדד הנגזר מהסטארט-אפים של Android App Startups ל-thread הראשי של האפליקציה.
  6. פלח המדדים הנגזרים מאפשר לראות בקלות מה בדיוק נכלל את ההפעלה של האפליקציה, כך שאפשר להמשיך לנפות באגים בצורה מפורטת יותר.

שימוש במדדים כדי לבדוק ולשפר חברות סטארט-אפ

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

יתרונות השימוש במדדי חברות סטארט-אפ

מערכת Android משתמשת בזמן להצגה הראשונית (TTID) ובזמן להצגה מלאה. (TTFD) כדי לבצע אופטימיזציה של אפליקציות במצב התחלתי (cold start) וחמות. זמן ריצה של Android (ART) משתמשת בנתונים מהמדדים האלה כדי לבצע הידור מראש של קוד ביעילות לצורך אופטימיזציה של חברות סטארט-אפ עתידיות.

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

תפקוד האפליקציה

התכונה 'תפקוד האפליקציה' יכולה לעזור לשפר את הביצועים של האפליקציה שלך באמצעות התראות Play Console כשזמני ההפעלה של האפליקציה ארוכים מדי.

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

  • הפעלה קרה נמשכת 5 שניות או יותר.
  • הפעלה חמימה נמשכת 2 שניות או יותר.
  • הפעלה חמה נמשכת דקה וחצי או יותר.

התכונה 'תפקוד האפליקציה' משתמשת במדד זמן להצגה ראשונית (TTID). עבור על האופן שבו מערכת Google Play אוספת נתוני תפקוד האפליקציה, אפשר לעיין ב מסמכי תיעוד של המסוף.

זמן עד להצגה ראשונית

הזמן עד להצגה הראשונית (TTID) הוא הזמן שעובר עד להצגת הפריים הראשון. בממשק המשתמש של האפליקציה. המדד הזה מודד את זמן הייצור של אפליקציה הפריים הראשון, כולל אתחול תהליך במהלך הפעלה במצב התחלתי (cold start), פעילות בזמן הפעלה במצב התחלתי (cold start) או במצב ביניים (warm start), והצגת הפריים הראשון. שמירה שהמדד 'מזהה נמוך' של האפליקציה שלך מאפשר למשתמשים לראות את חוויית המשתמש כדי לשפר את חוויית המשתמש של האפליקציה לפעול במהירות. הנתון הזה מדווח באופן אוטומטי לכל אפליקציה על ידי Android מסגרת. כשמבצעים אופטימיזציה להפעלת האפליקציה, מומלץ להטמיע reportFullyDrawn כדי לקבל מידע עד TTFD.

טבלת ה-TTDID נמדדת כערך זמן שמייצג את הזמן הכולל שחלף, כולל את רצף האירועים הבא:

  • הפעלת התהליך.
  • אתחול האובייקטים
  • יצירה ואתחול של הפעילות.
  • ניפוח הפריסה.
  • שרטוט של האפליקציה בפעם הראשונה.

אחזור VRID

כדי לאתר את אובייקט ה-TTD, אפשר לחפש קו פלט בכלי שורת הפקודה של Logcat שמכיל ערך בשם Displayed. הערך הזה הוא ה-TTDID והוא נראה דומה לדוגמה הבאה, שבה ה-TTDID הוא 3s534ms:

ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms

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

איור 5. מסננים מושבתים וDisplayed value ב-Logcat.

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

לפעמים השורה Displayed בפלט Logcat מכילה שדה נוסף לזמן הכולל. לדוגמה:

ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms (total +1m22s643ms)

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

מומלץ להשתמש ב-Logcat ב-Android Studio, אבל אם אתם לא משתמשים ב-Android ב-Studio, אפשר גם למדוד את מדד ה-TTD על ידי הפעלת האפליקציה באמצעות המעטפת adb" פקודה של מנהל הפעילות. הנה דוגמה:

adb [-d|-e|-s <serialNumber>] shell am start -S -W
com.example.app/.MainActivity
-c android.intent.category.LAUNCHER
-a android.intent.action.MAIN

המדד Displayed מופיע בפלט Logcat כמו קודם. המסוף שלך מציג את הנתונים הבאים:

Starting: Intent
Activity: com.example.app/.MainActivity
ThisTime: 2044
TotalTime: 2044
WaitTime: 2054
Complete

הארגומנטים -c ו--a הם אופציונליים ומאפשרים לך לציין <category> ו-<action>.

זמן עד להצגה מלאה

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

המערכת קובעת את-TTDID כאשר Choreographer קורא לפונקציה onDraw(), וכשהיא מזהה אותה בפעם הראשונה. עם זאת, המערכת לא יודעת מתי לזהות את ה-TTDFD, כי כל אפליקציה מתנהגת באופן שונה. כדי לזהות את טופס ה-TTD, האפליקציה צריכה לאותת למערכת כשהוא מגיע למצב המצויר במלואו.

אחזור TTFD

כדי למצוא את 'דברים שאפשר לעשות' (TTDFD), מסמנים את המצב המשורטט במלואו על ידי קריאה reportFullyDrawn() בשיטה ComponentActivity. שיטת reportFullyDrawn מדווחת כשהאפליקציה מאורגנת במלואה וניתן להשתמש בה . ה-TTDFD הוא הזמן שעובר מהרגע שהמערכת מקבלת את הפעלת האפליקציה Intent לכוונת ההפעלה של reportFullyDrawn(). אם לא תתקשרו reportFullyDrawn(), לא דווח ערך של 'דברים שאפשר לעשות' (TTFD).

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

כשמשתמשים ב-reportFullyDrawn(), Logcat מציג פלט כמו זה לדוגמה, שבה ה-TTDFD הוא 1s54ms:

system_process I/ActivityManager: Fully drawn {package}/.MainActivity: +1s54ms

פלט ה-Logcat כולל לפעמים זמן total, כפי שמתואר בזמן עד תצוגה ראשונית.

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

במקרים בסיסיים, אפשר להשתמש ב-reportFullyDrawn() כדי לסמן את המצב המשורטט במלואו כאשר מודעים לכך שהמצב שמשורטט במלואו מתקבל. אבל במקרים שבהם שרשורי הרקע חייבים להשלים את העבודה ברקע לפני השרטוט המלא הושג, יש לעכב את reportFullyDrawn() כדי לקבל מדידת TOFD. למידע נוסף על דחייה של reportFullyDrawn() אפשר לעיין במאמרים הבאים .

שיפור הדיוק של תזמון ההפעלה

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

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

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

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

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

Kotlin

class MainActivity : ComponentActivity() {

    sealed interface ActivityState {
        data object LOADING : ActivityState
        data object LOADED : ActivityState
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            var activityState by remember {
                mutableStateOf(ActivityState.LOADING as ActivityState)
            }
            fullyDrawnReporter.addOnReportDrawnListener {
                activityState = ActivityState.LOADED
            }
            ReportFullyDrawnTheme {
                when(activityState) {
                    is ActivityState.LOADING -> {
                        // Display the loading UI.
                    }
                    is ActivityState.LOADED -> {
                        // Display the full UI.
                    }
                }
            }
            SideEffect {
                lifecycleScope.launch(Dispatchers.IO) {
                    fullyDrawnReporter.addReporter()

                    // Perform the background operation.

                    fullyDrawnReporter.removeReporter()
                }
                lifecycleScope.launch(Dispatchers.IO) {
                    fullyDrawnReporter.addReporter()

                    // Perform the background operation.

                    fullyDrawnReporter.removeReporter()
                }
            }
        }
    }
}

Java

public class MainActivity extends ComponentActivity {
    private FullyDrawnReporter fullyDrawnReporter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        fullyDrawnReporter = getFullyDrawnReporter();
        fullyDrawnReporter.addOnReportDrawnListener(() -> {
            // Trigger the UI update.
            return Unit.INSTANCE;
        });

        new Thread(new Runnable() {
            @Override
            public void run() {
                fullyDrawnReporter.addReporter();

                // Do the background work.

               fullyDrawnReporter.removeReporter();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                fullyDrawnReporter.addReporter();

                // Do the background work.

                fullyDrawnReporter.removeReporter();
            }
        }).start();
    }
}

אם האפליקציה שלכם משתמשת ב-Jetpack פיתוח נייטיב, תוכלו להשתמש בממשקי ה-API הבאים כדי לציין מצב מלא:

  • ReportDrawn: מציין שהתוכן הקומפוזבילי מוכן מיד עבור אינטראקציה חוזרת.
  • ReportDrawnWhen: לוקח פרדיקט, כמו list.count > 0, ל- מציינים מתי התוכן הקומפוזבילי מוכן לאינטראקציה.
  • ReportDrawnAfter: משתמשת בשיטת השעיה, שבסיומה, מציין שהתוכן הקומפוזבילי מוכן לאינטראקציה.
זיהוי של צווארי בקבוק

כדי לחפש צווארי בקבוק, אפשר להשתמש בכלי לניתוח ביצועי המעבד (CPU) של Android Studio. לקבלת מידע נוסף למידע נוסף, ראו בדיקת הפעילות של המעבד (CPU) באמצעות הכלי לניתוח ביצועי ה-CPU.

אפשר גם לקבל תובנות לגבי צווארי בקבוק אפשריים באמצעות מעקב מוטבע בתוך האפליקציות והפעילויות onCreate() אמצעי תשלום. למידע נוסף על 'ערכים מוטבעים' במעקב, עיינו במסמכי התיעוד של הפונקציות Trace ובסקירה כללית של מעקב המערכת.

פתרון בעיות נפוצות

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

אתחול נרחב של האפליקציה

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

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

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

אבחון הבעיה

תוכלו להשתמש במעקב שיטה או במעקב מוטבע כדי לנסות לאבחן את הבעיה.

מעקב שיטה

הרצת הכלי לניתוח ביצועי ה-CPU חושפת שהcallApplicationOnCreate() method קוראת בסופו של דבר לשיטה com.example.customApplication.onCreate שלך. אם המיקום הכלי מראה שהשיטות האלה נמשכות זמן רב עד הביצוע של השיטות האלה, לחקור לעומק כדי לראות אילו עבודות מתבצעות שם.

מעקב פנימי

השתמשו במעקב מוטבע כדי לחקור את הסיבות האפשריות, כולל הפעולות הבאות:

  • הפונקציה onCreate() הראשונית של האפליקציה.
  • כל אובייקט גלובלי מסוג singleton שהאפליקציה שלך מפעילה.
  • כל קלט/פלט (I/O) של דיסק, פעולת deserialization או לולאות צפופות שעשויות להתרחש במהלך בצוואר הבקבוק.

פתרונות לבעיה

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

כדאי גם להשתמש ב-framework של החדרת תלות כמו Hilt יוצרת אובייקטים ויחסי תלות כשהם מוחדרים בפעם הראשונה.

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

אתחול של פעילות חזקה

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

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

אבחון הבעיה

גם במקרה זה, ניתן להשתמש במעקב באמצעות method וגם במעקב בתוך השורה.

מעקב שיטה

כשמשתמשים בכלי לניתוח נתוני המעבד (CPU), חשוב לשים לב לApplication של האפליקציה בנאים למחלקות משניות ו-com.example.customApplication.onCreate() methods.

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

מעקב פנימי

השתמשו במעקב מוטבע כדי לחקור את הסיבות האפשריות, כולל הפעולות הבאות:

  • פונקציית onCreate() הראשונית של האפליקציה.
  • כל אובייקט גלובלי מסוג singleton שהוא מאתחל.
  • כל קלט/פלט (I/O) של דיסק, פעולת deserialization או לולאות צפופות שעשויות להתרחש במהלך בצוואר הבקבוק.

פתרונות לבעיה

יש הרבה צווארי בקבוק אפשריים, אבל שתי בעיות נפוצות ופתרונות אפשריים: ככה:

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

מסכי פתיחה בהתאמה אישית

ייתכן שיתווסף זמן נוסף במהלך ההפעלה אם השתמשתם בעבר באחד מ בשיטות הבאות להטמעת מסך פתיחה בהתאמה אישית ב-Android 11 (API רמה 30) או מוקדם יותר:

  • שימוש במאפיין העיצוב windowDisablePreview כדי להשבית את מסך ריק ששורטט על ידי המערכת במהלך ההפעלה.
  • באמצעות Activity ייעודי.

החל מגרסה 12 של Android, נדרשת העברה ל-API של SplashScreen. ה-API הזה מאפשר זמן הפעלה מהיר יותר ומאפשר לבצע שינויים במסך הפתיחה בדרכים הבאות:

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

לפרטים נוספים, אפשר לעיין במדריך להעברת מסך ב-Splash.