המשתמשים מצפים שאפליקציות ייטענו במהירות ויהיו רספונסיביות. אפליקציה עם זמן הפעלה איטי לא עומדת בציפייה הזו ועלולה לאכזב את המשתמשים. חוויה כזו עלולה לגרום למשתמש לדרג את האפליקציה שלכם בצורה נמוכה בחנות Play, או אפילו לגרום לו להפסיק להשתמש באפליקציה לגמרי.
בדף הזה נספק מידע שיעזור לכם לבצע אופטימיזציה של זמן ההפעלה של האפליקציה, כולל סקירה כללית של הרכיבים הפנימיים של תהליך ההשקה, הסבר על יצירת פרופיל של ביצועי האפליקציה בזמן ההפעלה ובעיות נפוצות בזמן ההפעלה עם טיפים לפתרון הבעיות.
הסבר על המצבים השונים של הפעלת האפליקציה
הפעלת האפליקציה יכולה להתרחש באחד משלושת המצבים הבאים: הפעלה במצב התחלתי (cold start), הפעלה במצב ביניים (warm start) והפעלה במצב חם (hot start). כל מצב משפיע על משך הזמן שיחלוף עד שהאפליקציה תהיה גלויה למשתמש. בהפעלה מחדש, האפליקציה מתחילה מאפס. במצבים האחרים, המערכת צריכה להעביר את האפליקציה שפועלת מהרקע לחזית.
מומלץ תמיד לבצע אופטימיזציה על סמך הנחה של הפעלה ראשונית (cold start). כך אפשר גם לשפר את הביצועים של הפעלות חמות וחצי חמות.
כדי לבצע אופטימיזציה של האפליקציה להפעלה מהירה, כדאי להבין מה קורה ברמת המערכת וברמת האפליקציה ואיך הן פועלות יחד בכל אחד מהמצבים האלה.
שני מדדים חשובים לקביעת זמן ההפעלה של האפליקציה הם הזמן להצגה ראשונית (TTID) והזמן להצגה מלאה (TTFD). המדד TTID מייצג את הזמן שחולף עד להצגת הפריים הראשון, והמדד TTFD מייצג את הזמן שחולף עד שהאפליקציה הופכת לפעילה באופן מלא. שני המדדים חשובים באותה מידה, כי TTID מאפשר למשתמש לדעת שהאפליקציה נטענת, ו-TTFD מאפשר לו להשתמש באפליקציה בפועל. אם אחד מהם ארוך מדי, יכול להיות שהמשתמש ייצא מהאפליקציה עוד לפני שהיא תסתיים לטעינת הנתונים.
הפעלה במצב התחלתי (cold start)
הפעלה במצב התחלתי (cold start) מתייחסת להפעלה של אפליקציה מאפס. כלומר, עד להתחלה הזו, התהליך של המערכת יוצר את התהליך של האפליקציה. הפעלות מברירת המחדל מתרחשות במקרים כמו הפעלת האפליקציה בפעם הראשונה מאז שהמכשיר הופעל או מאז שהמערכת הפסיקה את האפליקציה.
סוג ההפעלה הזה הוא האתגר הגדול ביותר לקיצור זמן ההפעלה, כי למערכת ולאפליקציה יש יותר עבודה לעשות מאשר במצבי ההפעלה האחרים.
בתחילת ההפעלה במצב התחלתי, המערכת מבצעת את שלוש המשימות הבאות:
- טוענים ומפעילים את האפליקציה.
- להציג חלון התחלה ריק של האפליקציה מיד אחרי ההפעלה.
- יוצרים את תהליך האפליקציה.
ברגע שהמערכת יוצרת את תהליך האפליקציה, תהליך האפליקציה אחראי על השלבים הבאים:
- יוצרים את אובייקט האפליקציה.
- מפעילים את ה-thread הראשי.
- יוצרים את הפעילות הראשית.
- הגדלת מספר הצפיות.
- מגדירים את הפריסה של המסך.
- מבצעים את ההגרלה הראשונית.
כשתהליך האפליקציה משלים את הציור הראשון, תהליך המערכת מחליף את חלון הרקע המוצג בפעילות הראשית. בשלב הזה, המשתמש יכול להתחיל להשתמש באפליקציה.
באיור 1 מוצג איך המערכת והאפליקציה מעבירות משימות זו לזו.

בעיות בביצועים יכולות להתרחש במהלך יצירת האפליקציה וביצוע הפעילות.
יצירת אפליקציה
כשהאפליקציה מופעלת, החלון הריק של תחילת האפליקציה נשאר במסך עד שהמערכת מסיימת לצייר את האפליקציה בפעם הראשונה. בשלב הזה, תהליך המערכת מחליף את חלון ההתחלה של האפליקציה, ומאפשר למשתמש לבצע פעולות באפליקציה.
אם מבטלים את הגדרת ברירת המחדל של Application.onCreate()
באפליקציה שלכם, המערכת מפעילה את השיטה onCreate()
באובייקט האפליקציה. לאחר מכן, האפליקציה יוצרת את ה-thread הראשי, שנקרא גם thread של ממשק המשתמש, ומקצה לו את המשימה של יצירת הפעילות הראשית.
מכאן ואילך, התהליכים ברמת המערכת וברמת האפליקציה מתקדמים בהתאם לשלבי מחזור החיים של האפליקציה.
יצירת פעילות
אחרי שתהליך האפליקציה יוצר את הפעילות, הפעילות מבצעת את הפעולות הבאות:
- איפוס הערכים.
- קריאה למתודות היצירה.
- קריאה לשיטת ה-callback, כמו
Activity.onCreate()
, בהתאם למצב הנוכחי של מחזור החיים של הפעילות.
בדרך כלל, השיטה onCreate()
משפיעה בצורה המשמעותית ביותר על זמן הטעינה, כי היא מבצעת את העבודה עם התקורה הגבוהה ביותר: טעינת תצוגות וניפוח שלהן, ויצירת אובייקטים ראשונית שנדרשים להפעלת הפעילות.
הפעלה במצב ביניים (warm start)
הפעלה במצב ביניים כוללת קבוצת משנה של הפעולות שמתרחשות במהלך הפעלה מחדש. עם זאת, היא מייצגת עלות ריבית תפעולית גבוהה יותר מאשר הפעלה מתוך הזיכרון (hot start). יש הרבה מצבים אפשריים שאפשר להתייחס אליהם כהתחלה חמה, למשל:
המשתמש יוצא מהאפליקציה אבל מפעיל אותה מחדש. יכול להיות שהתהליך ימשיך לפעול, אבל האפליקציה תצטרך ליצור מחדש את הפעילות מאפס באמצעות קריאה ל-
onCreate()
.המערכת מסירה את האפליקציה מהזיכרון, ואז המשתמש מפעיל אותה מחדש. התהליך והפעילות צריכים להתחיל מחדש, אבל המשימה יכולה ליהנות במידה מסוימת מחבילת המצב של המכונה השמורה שהועברה אל
onCreate()
.
הפעלה מתוך הזיכרון (hot start)
להפעלה מתוך הזיכרון (hot start) של האפליקציה יש עלות תפעול נמוכה יותר מאשר להפעלה במצב התחלתי (cold start). בהפעלה חמה, המערכת מעבירה את הפעילות שלכם לחזית. אם כל הפעילויות של האפליקציה עדיין נמצאות בזיכרון, האפליקציה יכולה להימנע מחזור של אתחול אובייקטים, הרחבת פריסה ורינדור.
עם זאת, אם חלק מהזיכרון נמחק בתגובה לאירועים של חיתוך זיכרון, כמו onTrimMemory()
, צריך ליצור מחדש את האובייקטים האלה בתגובה לאירוע ההפעלה החמה.
בסשן עם הפעלה מתוך הזיכרון (hot start) מוצגת אותה התנהגות במסך כמו בתרחיש של הפעלה במצב התחלתי (cold start). בתהליך המערכת מוצג מסך ריק עד שהאפליקציה מסיימת את העיבוד של הפעילות.

איך מזהים את הפעלת האפליקציה ב-Perfetto
כדי לנפות באגים בבעיות בהפעלת האפליקציה, כדאי להבין מה בדיוק נכלל בשלב ההפעלה של האפליקציה. כדי לזהות את כל שלב ההפעלה של האפליקציה ב-Perfetto, פועלים לפי השלבים הבאים:
ב-Perfetto, מאתרים את השורה עם המדד המורכב 'הפעלות של אפליקציות ל-Android'. אם הוא לא מופיע, אפשר לנסות לצלם מעקב באמצעות אפליקציית המעקב אחר המערכת במכשיר.
איור 3. פרוסת המדד 'הפעלות של אפליקציות ל-Android' שמבוססת על Perfetto. לוחצים על הפרוסה המשויכת ומקישים על m כדי לבחור אותה. סוגריים מופיעים סביב החלק הזה ומציינים את משך הזמן שלקח לו להתבצע. משך הזמן מוצג גם בכרטיסייה Current selection.
כדי להצמיד את השורה 'סטארט-אפים של אפליקציות ל-Android', לוחצים על סמל ההצמדה שמופיע כשמחזיקים את הסמן מעל השורה.
גוללים לשורה עם האפליקציה הרלוונטית ולוחצים על התא הראשון כדי להרחיב את השורה.
כדי להתקרב לשרשור הראשי, בדרך כלל בחלק העליון, מקישים על w (מקישים על s, a, d כדי להתרחק, לזוז ימינה או שמאלה, בהתאמה).
איור 4.הפלח של המדד המורכב 'הפעלות ראשוניות של אפליקציות ל-Android' לצד השרשור הראשי של האפליקציה. באמצעות פרוסת המדדים המשוערים קל יותר לראות מה בדיוק נכלל בהפעלת האפליקציה, וכך אפשר להמשיך לנפות באגים בפירוט רב יותר.
שימוש במדדים כדי לבדוק ולשפר חברות סטארט-אפ
כדי לאבחן בצורה נכונה את הביצועים של זמן ההפעלה, אפשר לעקוב אחרי מדדים שמראים כמה זמן לוקח לאפליקציה להתחיל לפעול. ב-Android יש כמה דרכים להראות לכם שיש בעיה באפליקציה ולעזור לכם לאבחן אותה. 'נתונים חיוניים ל-Android' יכולים להתריע על בעיה, וכלים לאבחון יכולים לעזור לכם לאבחן את הבעיה.
היתרונות של שימוש במדדים של חברות סטארט-אפ
מערכת Android משתמשת במדדים זמן להצגה ראשונית (TTID) וזמן להצגה מלאה (TTFD) כדי לבצע אופטימיזציה של הפעלות ראשוניות של אפליקציות והפעלות חוזרות של אפליקציות. Android Runtime (ART) משתמש בנתונים מהמדדים האלה כדי לבצע הידור מראש של קוד ביעילות, לצורך אופטימיזציה של סטארט-אפים עתידיים.
הפעלה מהירה יותר מובילה לאינטראקציה ממושכת יותר של המשתמשים עם האפליקציה, וכך מפחיתה את מספר המקרים שבהם המשתמשים יוצאים מהאפליקציה מוקדם מדי, מפעילים מחדש את המכונה או עוברים לאפליקציה אחרת.
תפקוד האפליקציה
נתוני תפקוד האפליקציה ב-Android יכולים לעזור לכם לשפר את הביצועים של האפליקציה. הם מאפשרים לכם לקבל התראות ב-Play Console כשזמני ההפעלה של האפליקציה ארוכים מדי.
מערכת 'תפקוד האפליקציה' מתייחסת לזמני ההפעלה הבאים של האפליקציה שלכם כזמנים מוגזמים:
- זמן ההפעלה הראשוני נמשך 5 שניות או יותר.
- הפעלה במצב ביניים נמשכת 2 שניות או יותר.
- זמן ההפעלה מתוך הזיכרון נמשך 1.5 שניות או יותר.
במדדי 'תפקוד האפליקציה' ל-Android נעשה שימוש במדד זמן עד להצגה הראשונית (TTID). במסמכי העזרה של Play Console מוסבר איך Google Play אוספת את נתוני תפקוד האפליקציה ב-Android.
הזמן עד להצגה הראשונית
זמן ההצגה הראשונית (TTID) הוא הזמן שחולף עד להצגת הפריים הראשון של ממשק המשתמש של האפליקציה. המדד הזה מודד את הזמן שלוקח לאפליקציה ליצור את המסגרת הראשונה, כולל את האיפוס של התהליך במהלך הפעלה במצב התחלתי (cold start), יצירת הפעילות במהלך הפעלה במצב התחלתי או הפעלה מחדש (warm start) והצגת המסגרת הראשונה. כדי לשפר את חוויית המשתמש, מומלץ שהזמן שיחלף מהקליק על מודעה להפעלת האפליקציה יהיה קצר. המערכת של Android Framework מדווחת על TTID באופן אוטומטי לכל אפליקציה. כשמבצעים אופטימיזציה להפעלת האפליקציה, מומלץ להטמיע את הערך reportFullyDrawn
כדי לקבל מידע עד TTFD.
המדד TTID נמדד כערך זמן שמייצג את משך הזמן הכולל שחלף, כולל רצף האירועים הבא:
- הפעלת התהליך.
- אתחול האובייקטים.
- יצירת הפעילות והפעלתה.
- הרחבת הפריסה.
- ציור האפליקציה בפעם הראשונה.
אחזור TTID
כדי למצוא את TTID, מחפשים בכלי שורת הפקודה Logcat שורת פלט שמכילה את הערך Displayed
. הערך הזה הוא ה-TTID, והוא נראה דומה לדוגמה הבאה, שבה ה-TTID הוא 3s534ms:
ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms
כדי למצוא את TTID ב-Android Studio, משביתים את המסננים בתצוגת Logcat בתפריט הנפתח של המסננים, ואז מחפשים את השעה Displayed
, כפי שמוצג באיור 5.
צריך להשבית את המסננים כי יומן המערכת הזה מוצג על ידי שרת המערכת, ולא על ידי האפליקציה עצמה.

Displayed
ב-logcat.המדד Displayed
בפלט של Logcat לא מתעד בהכרח את משך הזמן עד שכל המשאבים נטענים ומוצגים. הוא לא כולל משאבים שלא מופיעה להם הפניה בקובץ הפריסה, או משאבים שנוצרים על ידי האפליקציה כחלק מהפעלת האובייקט. המשאבים האלה לא נכללים כי הטעינה שלהם היא תהליך מוטמע, והיא לא חוסמת את התצוגה הראשונית של האפליקציה.
לפעמים השורה Displayed
בפלט של Logcat מכילה שדה נוסף לזמן הכולל. לדוגמה:
ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms (total +1m22s643ms)
במקרה כזה, המדידה הראשונה היא רק לגבי הפעילות שצוירה קודם. מדידת הזמן של total
מתחילה עם תחילת תהליך האפליקציה, ויכולה לכלול פעילות אחרת שהתחילה קודם אבל לא הציגה שום דבר במסך. מדידת הזמן total
מוצגת רק אם יש הבדל בין הפעילות היחידה לבין זמני ההפעלה הכוללים.
מומלץ להשתמש ב-Logcat ב-Android Studio, אבל אם אתם לא משתמשים ב-Android Studio, תוכלו למדוד את TTID גם על ידי הפעלת האפליקציה באמצעות הפקודה 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 כמו קודם. בחלון מסוף ה-CLI יופיעו הפרטים הבאים:
Starting: Intent
Activity: com.example.app/.MainActivity
ThisTime: 2044
TotalTime: 2044
WaitTime: 2054
Complete
הארגומנטים -c
ו--a
הם אופציונליים ומאפשרים לציין את <category>
ואת <action>
.
זמן ההצגה במסך מלא
הזמן עד להצגה מלאה (TTFD) הוא משך הזמן שחולף עד שהאפליקציה מאפשרת אינטראקציה למשתמש. הזמן הזה מדווח כזמן שנדרש להצגת הפריים הראשון של ממשק המשתמש של האפליקציה, וגם של התוכן שנטען באופן אסינכרוני אחרי הצגת הפריים הראשוני. בדרך כלל, מדובר בתוכן הראשי שנטען מהרשת או מהדיסק, כפי שמדווח על ידי האפליקציה. במילים אחרות, TTFD כולל את TTID ואת הזמן שנדרש עד שאפשר להשתמש באפליקציה. כשזמן התגובה הראשוני של האפליקציה קצר, המשתמשים יכולים לבצע פעולות באפליקציה במהירות, וכך חוויית המשתמש משתפרת.
המערכת קובעת את ה-TTID כש-Choreographer
קורא לשיטה onDraw()
של הפעילות, וכשהיא יודעת שהקריאה היא בפעם הראשונה.
עם זאת, המערכת לא יודעת מתי לקבוע את זמן הטעינה הממוצע כי כל אפליקציה מתנהגת בצורה שונה. כדי לקבוע את זמן הטעינה הממוצע, האפליקציה צריכה לשלוח אות למערכת כשהיא מגיעה למצב שבו היא מצוירת במלואה.
אחזור TTFD
כדי למצוא את TTFD, מדווחים על המצב שבו התמונה מצוירת במלואה על ידי קריאה לשיטה reportFullyDrawn()
של ComponentActivity
. השיטה reportFullyDrawn
מדווחת מתי האפליקציה מצוירת במלואה ובמצב שמאפשר להשתמש בה. TTFD הוא הזמן שחלף מהרגע שבו המערכת מקבלת את הכוונה להפעלת האפליקציה ועד לקריאה ל-reportFullyDrawn()
. אם לא קוראים ל-reportFullyDrawn()
, לא מדווחים על ערך TTFD.
כדי למדוד את TTFD, צריך לקרוא ל-reportFullyDrawn()
אחרי שמשלימים את תצוגת הממשק המשתמש ואת כל הנתונים. אין להפעיל את reportFullyDrawn()
לפני שחלון הפעילות הראשונה מצויר ומוצג בפעם הראשונה, כפי שנמדד על ידי המערכת, כי אז המערכת מדווחת על הזמן שנמדד על ידי המערכת. במילים אחרות, אם קוראים לפונקציה reportFullyDrawn()
לפני שהמערכת מזהה את TTID, המערכת מדווחת על TTID ועל TTFD כאותו ערך, והערך הזה הוא ערך ה-TTID.
כשמשתמשים ב-reportFullyDrawn()
, ב-Logcat מוצג פלט כמו בדוגמה הבאה, שבה זמן ה-TTFD הוא 1s54ms:
system_process I/ActivityManager: Fully drawn {package}/.MainActivity: +1s54ms
לפעמים הפלט של Logcat כולל את הזמן total
, כפי שמתואר בקטע הזמן עד להצגה הראשונית.
אם זמני ההצגה איטיים יותר ממה שרצוי, אפשר לנסות לזהות את צוואר הבקבוק בתהליך ההפעלה.
אפשר להשתמש ב-reportFullyDrawn()
כדי לסמן את המצב 'הושלם תהליך הציור' במקרים בסיסיים שבהם אתם יודעים שהמצב הזה הושג. עם זאת, במקרים שבהם חוטי הרקע צריכים להשלים את העבודה ברקע לפני שמגיעים למצב שבו התמונה מצוירת במלואה, צריך לעכב את reportFullyDrawn()
כדי לקבל מדידה מדויקת יותר של TTFD. בקטע הבא מוסבר איך לעכב את 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 {
fullyDrawnReporter.addReporter()
lifecycleScope.launch(Dispatchers.IO) {
// Perform the background operation.
fullyDrawnReporter.removeReporter()
}
fullyDrawnReporter.addReporter()
lifecycleScope.launch(Dispatchers.IO) {
// 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 Compose, תוכלו להשתמש בממשקי ה-API הבאים כדי לציין מצב שבו התמונה מצוירת במלואה:
ReportDrawn
: מציין שהרכיב ה-Composable מוכן מיד לאינטראקציה.ReportDrawnWhen
: מקבלת תנאי, כמוlist.count > 0
, כדי לציין מתי ה-Composable מוכן לאינטראקציה.ReportDrawnAfter
: מקבלת שיטה להשהיה, שבסיום ההפעלה שלה מציינת שהרכיב המודולרי מוכן לאינטראקציה.
זיהוי צווארי בקבוק
כדי לחפש צווארי בקבוק, אפשר להשתמש בכלי לניתוח ביצועים של מעבד (CPU Profiler) ב-Android Studio. למידע נוסף, ראו בדיקת פעילות המעבד באמצעות כלי לניתוחי מעבדים.
אפשר גם לקבל תובנות לגבי צווארי בקבוק פוטנציאליים באמצעות מעקב בתוך שורות (inline) בתוך השיטות onCreate()
של האפליקציות והפעילויות. מידע נוסף על ניתוח בקוד מופיע במסמכים של הפונקציות Trace
ובסקירה הכללית על ניתוח נתוני מערכת.
פתרון בעיות נפוצות
בקטע הזה נסביר על כמה בעיות שמשפיעות לעיתים קרובות על ביצועי האפליקציה בזמן ההפעלה. הבעיות האלה קשורות בעיקר לאינטוליזציה של אובייקטים של אפליקציות ופעילויות, וגם לטעינה של המסכים.
אתחול כבד של אפליקציה
הביצועים בזמן ההשקה עשויים להיפגע אם הקוד שלכם מבטל את הגדרת ברירת המחדל של האובייקט Application
ומבצע עבודה כבדה או לוגיקה מורכבת במהלך האיניציאליזציה של האובייקט. יכול להיות שהאפליקציה תבזבז זמן במהלך ההפעלה אם תת-הקלאסות של Application
מבצעות פעולות איפוס שעדיין לא צריך לבצע.
יכול להיות שחלק מהפעולות של יצירת המצב הראשוני לא נחוצות בכלל, למשל כשמפעילים את המידע על המצב של הפעילות הראשית כשהאפליקציה מופעלת בפועל בתגובה לכוונה (intent). כשמשתמשים בכוונה, האפליקציה משתמשת רק בקבוצת משנה של נתוני המצב שהותחלתו בעבר.
אתגרים נוספים במהלך האינטלייזציה של האפליקציה כוללים אירועי איסוף אשפה משמעותיים או רבים, או פעולות קלט/פלט בדיסק שמתרחשות במקביל לאינטליזציה, וחוסמות עוד יותר את תהליך האינטלייזציה. חשוב להביא בחשבון את האיסוף של 'זבל' במיוחד בסביבת זמן הריצה של Dalvik. בסביבת זמן הריצה של Android (ART), האיסוף של 'זבל' מתבצע בו-זמנית, וכך מצמצמים את ההשפעה של הפעולה הזו.
אבחון הבעיה
אפשר להשתמש במעקב אחר שיטות או במעקב בתוך שורות קוד כדי לנסות לאבחן את הבעיה.
תיעוד שיטות
הפעלת הכלי לניתוח מעבדים חושפת שבסופו של דבר השיטה callApplicationOnCreate()
קורא לשיטה com.example.customApplication.onCreate
. אם בכלי מוצג שההפעלה של השיטות האלה נמשכת זמן רב, כדאי לבדוק מה מתבצע שם.
מעקב בקוד
אפשר להשתמש במעקב בקוד כדי לבדוק את הגורמים האפשריים לבעיה, כולל:
- הפונקציה הראשונית
onCreate()
של האפליקציה. - אובייקטים גלובלים מסוג singleton שהאפליקציה מפעילה.
- כל פעולות הקלט/פלט של הדיסק, ביטול הסריאליזציה או לולאות צפופות שעשויות להתרחש במהלך צווארון בקבוק.
פתרונות לבעיה
בין שהבעיה נובעת מהפעלות מיותרות של התחלה ובין שהיא נובעת מ-I/O בדיסק, הפתרון הוא הפעלה עצלה. במילים אחרות, צריך לאתחל רק אובייקטים שנדרשים באופן מיידי. במקום ליצור אובייקטים סטטיים גלובליים, כדאי לעבור לדפוס של מופע יחיד (singleton) שבו האפליקציה מאתחלת אובייקטים רק בפעם הראשונה שהיא זקוקה להם.
מומלץ גם להשתמש במסגרת להזרקת יחסי תלות כמו Hilt, שמאפשרת ליצור אובייקטים ויחסי תלות כשהם מוזרקים בפעם הראשונה.
אם באפליקציה שלכם נעשה שימוש בספקי תוכן כדי לאתחל את רכיבי האפליקציה בזמן ההפעלה, מומלץ להשתמש במקום זאת בספריית ההפעלה של האפליקציה.
אתחול של פעילות כבדה
יצירת פעילות כזו כרוכה לרוב בעבודה רבה עם עלות גבוהה. בדרך כלל יש הזדמנויות לבצע אופטימיזציה של העבודה הזו כדי לשפר את הביצועים. דוגמאות לבעיות נפוצות כאלה:
- הגדלת פריסות גדולות או מורכבות.
- חסימה של ציור המסך בדיסק או של קלט/פלט ברשת.
- טעינה ופענוח של קובצי bitmap.
- יצירת רסטר של אובייקטים מסוג
VectorDrawable
. - אתחול של תת-מערכות אחרות של הפעילות.
אבחון הבעיה
גם במקרה הזה, אפשר להשתמש גם במעקב אחר שיטות וגם במעקב בקוד.
תיעוד שיטות
כשמשתמשים ב-CPU Profiler, חשוב לשים לב ל-methods ול-constructors של Application
של תת-הסוגים של האפליקציה.com.example.customApplication.onCreate()
אם בכלי מוצג שהשיטות האלה נמשכות זמן רב, כדאי לבדוק מהן הפעולות שמתבצעות שם.
מעקב בקוד
אפשר להשתמש במעקב בקוד כדי לבדוק את הגורמים האפשריים לבעיה, כולל:
- הפונקציה
onCreate()
הראשונית של האפליקציה. - כל אובייקט יחיד (singleton) גלובלי שהוא מאתחלל.
- כל פעולות הקלט/פלט של הדיסק, ביטול הסריאליזציה או לולאות צפופות שעשויות להתרחש במהלך צווארון בקבוק.
פתרונות לבעיה
יש הרבה צווארי בקבוק פוטנציאליים, אבל שתי בעיות נפוצות ופתרונות נפוצים הן:
- ככל שההיררכיה של התצוגה גדולה יותר, כך האפליקציה תידרש יותר זמן כדי לנפח אותה. כדי לפתור את הבעיה, אפשר לבצע את הפעולות הבאות:
- צמצום של פריסות מיותרות או פריסות בתצוגת עץ כדי לפשט את היררכיית התצוגה.
- אל תגדילו חלקים בממשק המשתמש שלא צריכים להיות גלויים במהלך ההשקה.
במקום זאת, השתמשו באובייקט
ViewStub
כ placeholder להיררכיות משנה שהאפליקציה יכולה לנפח בזמן מתאים יותר.
- גם ביצוע כל האיניציאליזציה של המשאבים בשרשור הראשי יכול להאט את ההפעלה. כדי לפתור את הבעיה הזו:
- העברת כל האיניציאליזציה של המשאבים כדי שהאפליקציה תוכל לבצע אותה באופן עצל בשרשור אחר.
- נותנים לאפליקציה לטעון ולהציג את התצוגות, ואז מעדכנים מאוחר יותר את המאפיינים החזותיים שתלויים בתמונות וביתר המשאבים.
מסכי פתיחה בהתאמה אישית
יכול להיות שתראו זמן נוסף שנוסף במהלך ההפעלה אם השתמשתם בעבר באחת מהשיטות הבאות כדי להטמיע מסך פתיחה בהתאמה אישית ב-Android 11 (רמת API 30) או בגרסאות קודמות:
- שימוש במאפיין העיצוב
windowDisablePreview
כדי להשבית את המסך הריק הראשוני שמוצג על ידי המערכת במהלך ההפעלה. - שימוש ב-
Activity
ייעודי.
החל מגרסה Android 12, צריך לעבור ל-API של SplashScreen
.
ה-API הזה מאפשר זמן הפעלה מהיר יותר ומאפשר לשנות את מסך הפתיחה בדרכים הבאות:
- מגדירים עיצוב כדי לשנות את המראה של מסך הפתיחה.
- אפשר לקבוע כמה זמן יוצג מסך הפתיחה באמצעות
windowSplashScreenAnimationDuration
. - להתאים אישית את האנימציה של מסך הפתיחה ולנהל בצורה חלקה את האנימציה לסגירת מסך הפתיחה.
בנוסף, ספריית התאימות מעבירה לאחור את SplashScreen
API כדי לאפשר תאימות לאחור וליצור מראה ועיצוב עקביים לתצוגת המסך הראשי בכל הגרסאות של Android.
פרטים נוספים זמינים במדריך להעברת מסך הפתיחה.
מומלץ עבורך
- הערה: טקסט הקישור מוצג כש-JavaScript מושבת
- עיבוד איטי
- תיעוד מדדי Macrobenchmark
- יצירת פרופילים Baseline{:#creating-profile-rules}