מדידת זמן הטעינה ורינדור המסך באמצעות מעקב אחר ביצועים ב-Firebase

1. מבוא

עדכון אחרון: 11 במרץ 2021

למה צריך למדוד את הביצועים של הצפיות?

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

האם מעקב אחרי ביצועים ב-Firebase לא מספק את מדדי הביצועים האלה כברירת מחדל?

הכלי 'ניטור ביצועים' ב-Firebase מתעד באופן אוטומטי חלק מנתוני הביצועים, כמו זמן ההפעלה של האפליקציה (כלומר, זמן הטעינה של הפעילות הראשונה בלבד) וביצועי העיבוד של המסך (כלומר, פריימים איטיים וקפואים לפעילויות, אבל לא ל-Fragments). עם זאת, באפליקציות לתעשייה בדרך כלל אין הרבה פעילויות, אלא פעילות אחת וכמה רכיבי Fragment. בנוסף, אפליקציות רבות בדרך כלל מטמיעות תצוגות מותאמות אישית משלהן לתרחישי שימוש מורכבים יותר. לכן, כדאי להבין איך למדוד את זמן הטעינה ואת ביצועי העיבוד של המסך גם של Activities וגם של Fragments, באמצעות מעקב אחר קטעי קוד מותאמים אישית באפליקציה. אפשר להרחיב בקלות את ה-codelab הזה כדי למדוד את הביצועים של רכיבי Custom View.

מה תלמדו

  • איך מוסיפים מעקב אחר ביצועים ב-Firebase לאפליקציית Android
  • הסבר על הטעינה של Activity או Fragment
  • איך מטמיעים מעקב אחר קוד מותאם אישית כדי למדוד את זמן הטעינה של פעילות או של Fragment
  • הסבר על עיבוד המסך ומהי מסגרת איטית או קפואה
  • איך מטמיעים מדדים במעקב אחר קוד מותאם אישית כדי לתעד מסכים איטיים או קפואים
  • איך רואים את המדדים שנאספו במסוף Firebase

מה צריך

  • ‫Android Studio מגרסה 4.0 ואילך
  • מכשיר Android או אמולטור
  • ‫Java מגרסה 8 ואילך

2. תהליך ההגדרה

קבלת הקוד

מריצים את הפקודות הבאות כדי לשכפל את קוד הדוגמה של ה-codelab הזה. תיקייה בשם codelab-measure-android-view-performance תיווצר במחשב:

$ git clone https://github.com/FirebaseExtended/codelab-measure-android-view-performance.git
$ cd codelab-measure-android-view-performance

אם אין לכם git במחשב, אתם יכולים גם להוריד את הקוד ישירות מ-GitHub.

מייבאים את הפרויקט measure-view-performance-start ל-Android Studio. סביר להניח שיופיעו שגיאות קומפילציה או אזהרה לגבי קובץ google-services.json חסר. נפתור את הבעיה הזו בקטע הבא של השלב הזה.

ב-codelab הזה נשתמש בפלאגין Firebase Assistant כדי לרשום את אפליקציית Android שלנו בפרויקט Firebase ולהוסיף לפרויקט Android את קובצי ההגדרות, הפלאגינים והתלות הדרושים של Firebase – הכול מתוך Android Studio!

קישור האפליקציה ל-Firebase

  1. כדי לוודא שאתם משתמשים בגרסאות העדכניות של Android Studio ושל Firebase Assistant, עוברים אל Android Studio/Help (עזרה) > Check for updates (בדיקת עדכונים).
  2. בוחרים באפשרות כלים > Firebase כדי לפתוח את החלונית Assistant (עזרה).
    e791bed0999db1e0.png
  3. בוחרים באפשרות מעקב אחר ביצועים כדי להוסיף אותה לאפליקציה, ואז לוחצים על תחילת העבודה עם מעקב אחר ביצועים.
  4. לוחצים על הלחצן ליצירת פרויקט חדש ומזינים שם לפרויקט (לדוגמה, Measure Performance Codelab).
  5. לוחצים על המשך.
  6. אם מוצגת בקשה לעשות זאת, קוראים ומאשרים את התנאים של Firebase, ואז לוחצים על המשך.
  7. (אופציונלי) מפעילים את העזרה מבוססת-AI במסוף Firebase (שנקראת Gemini ב-Firebase).
  8. ב-codelab הזה לא צריך להשתמש ב-Google Analytics, ולכן משביתים את האפשרות Google Analytics.
  9. אחרי כן אמור להופיע תיבת דו-שיח עם האפשרות Connect (קישור) של אפליקציית Firebase החדשה לפרויקט Android Studio.
    42c498d28ead2b77.png
  10. ב-Android Studio, בחלונית Assistant, אמור להופיע אישור שהאפליקציה שלכם מחוברת ל-Firebase.
    dda8bdd9488167a0.png

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

בחלונית Assistant ב-Android Studio, לוחצים על Add Performance Monitoring to your app.

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

9b58145acc4be030.png

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

aa0d46fc944e0c0b.png

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

3. הפעלת האפליקציה

אם שילבתם את האפליקציה בהצלחה עם Performance Monitoring SDK, הפרויקט אמור להתקמפל עכשיו. ב-Android Studio, לוחצים על Run (הפעלה) > Run ‘app' (הפעלת האפליקציה) כדי ליצור ולהפעיל את האפליקציה במכשיר Android המחובר או באמולטור.

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

410d8686b4f45c33.png

בשלבים הבאים של ה-codelab הזה תלמדו איך למדוד את זמן הטעינה ואת ביצועי העיבוד של המסך של הפעילות או של ה-Fragment.

4. הסבר על טעינה של Activity או Fragment

בשלב הזה נלמד מה המערכת עושה במהלך הטעינה של פעילות או של Fragment.

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

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

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

c20d14b151549937.png

הסבר על טעינה של Fragment

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

5. מדידת זמן הטעינה של פעילות

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

  1. מתחילים את המעקב אחר הקוד המותאם אישית (שנקרא TestActivity-LoadTime) בכיתת הפעילות ברגע שאובייקט הפעילות נוצר.

TestActivity.java

public class TestActivity extends AppCompatActivity {   
    // TODO (1): Start trace recording as soon as the Activity object is created.
    private final Trace viewLoadTrace = FirebasePerformance.startTrace("TestActivity-LoadTime");

    // ...

}
  1. מחליפים את onCreate()callback ומקבלים את התצוגה שנוספה על ידי השיטה setContentView().
@Override     
public void onCreate(Bundle savedInstanceState) {    
    super.onCreate(savedInstanceState);          

    // Current Activity's main View (as defined in the layout xml file) is inflated after this            
    setContentView(R.layout.activity_test);          

    // ...

    // TODO (2): Get the View added by Activity's setContentView() method.         
    View mainView = findViewById(android.R.id.content);     

    // ...
}
  1. הוספנו הטמעה של FistDrawListener, שכוללת שתי קריאות חוזרות (callback):‏ onDrawingStart() ו-onDrawingFinish() (בקטע הבא מפורט מידע נוסף על FirstDrawListener ועל הגורמים שיכולים להשפיע על הביצועים שלה). מבצעים רישום של FirstDrawListener בסוף הקריאה החוזרת של Activity onCreate(). צריך להפסיק את viewLoadTrace ב-onDrawingFinish()callback.

TestActivity.java

    // TODO (3): Register the callback to listen for first frame rendering (see
    //  "OnFirstDrawCallback" in FirstDrawListener) and stop the trace when View drawing is
    //  finished.
    FirstDrawListener.registerFirstDrawListener(mainView, new FirstDrawListener.OnFirstDrawCallback() {              
        @Override             
        public void onDrawingStart() {       
          // In practice you can also record this event separately
        }

        @Override             
        public void onDrawingFinish() {
            // This is when the Activity UI is completely drawn on the screen
            viewLoadTrace.stop();             
        }         
    });
  1. מריצים מחדש את האפליקציה ומסננים את logcat לפי מדד מעקב אחר רישום ביומן. מקישים על הלחצן LOAD ACTIVITY ומחפשים יומנים כמו אלה שמופיעים למטה:
I/FirebasePerformance: Logging trace metric: TestActivity-LoadTime (duration: XXXms)

‫🎉 איזה כיף! הצלחתם למדוד את זמן הטעינה של פעילות ולדווח על הנתונים האלה ל-Firebase Performance Monitoring. בהמשך ה-codelab נראה את המדד שתועד במסוף Firebase.

מטרת FirstDrawListener

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

הוא מיישם את ViewTreeObserver.OnDrawListener ומבטל את הקריאה החוזרת onDraw() שמופעלת כשעץ התצוגה עומד להימשך. לאחר מכן, המערכת עוטפת את התוצאה כדי לספק שתי פונקציות קריאה חוזרת (callback) של כלי עזר: onDrawingStart() ו-onDrawingFinish().

הקוד המלא של FirstDrawListener מופיע בקוד המקור של ה-codelab הזה.

6. מדידת זמן הטעינה של Fragment

מדידת זמן הטעינה של Fragment דומה למדידה של Activity, אבל יש כמה הבדלים קטנים. שוב, נגדיר מעקב אחר קוד בהתאמה אישית:

  1. עוקפים את onAttach() callback ומתחילים להקליט את fragmentLoadTrace. ניתן לטראס הזה את השם Test-Fragment-LoadTime.

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

TestFragment.java

public class TestFragment extends Fragment {

   // TODO (1): Declare the Trace variable.
   private Trace fragmentLoadTrace;

   @Override
   public void onAttach(@NonNull Context context) {
       super.onAttach(context);

       // TODO (2): Start trace recording as soon as the Fragment is attached to its host Activity.
       fragmentLoadTrace = FirebasePerformance.startTrace("TestFragment-LoadTime");
   }
  1. רושמים את FirstDrawListener ב-onViewCreated()callback. לאחר מכן, בדומה לדוגמה של הפעילות, מפסיקים את המעקב ב-onDrawingFinish().

TestFragment.java

@Override
public void onViewCreated(@NonNull View mainView, Bundle savedInstanceState) {
   super.onViewCreated(mainView, savedInstanceState);

   // ...

   // TODO (3): Register the callback to listen for first frame rendering (see
   //  "OnFirstDrawCallback" in FirstDrawListener) and stop the trace when view drawing is
   //  finished.
   FirstDrawListener.registerFirstDrawListener(mainView, new FirstDrawListener.OnFirstDrawCallback() {

       @Override
       public void onDrawingStart() {
           // In practice you can also record this event separately
       }

       @Override
       public void onDrawingFinish() {
           // This is when the Fragment UI is completely drawn on the screen
           fragmentLoadTrace.stop();
       }
   });
  1. מריצים מחדש את האפליקציה ומסננים את logcat לפי מדד מעקב אחר רישום ביומן. מקישים על הלחצן LOAD FRAGMENT ומחפשים יומנים כמו אלה שמופיעים למטה:
I/FirebasePerformance: Logging trace metric: TestFragment-LoadTime (duration: XXXms)

‫🎉 איזה כיף! מדדתם בהצלחה את זמן הטעינה של Fragment ודיווחתם על הנתונים האלה ל-Firebase Performance Monitoring. בהמשך ה-codelab נראה את המדד שתועד במסוף Firebase.

7. הסבר על עיבוד המסך ומהי מסגרת איטית או קפואה

רינדור של ממשק המשתמש הוא פעולה של יצירת פריים מהאפליקציה והצגתו על המסך. כדי לוודא שהאינטראקציה של המשתמש עם האפליקציה תהיה חלקה, האפליקציה צריכה לעבד פריימים בפחות מ-16 אלפיות השנייה כדי להגיע ל-60 פריימים לשנייה ( למה 60 פריימים לשנייה?). אם העיבוד של ממשק המשתמש באפליקציה איטי, המערכת נאלצת לדלג על פריימים, והמשתמש יחווה גמגום באפליקציה. אנחנו קוראים לזה גמגום.

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

8. מדידת פריימים איטיים או קפואים של קטע

הכלי 'מעקב אחר ביצועים ב-Firebase' מתעד באופן אוטומטי פריימים איטיים או קפואים של פעילות (אבל רק אם היא מואצת על ידי חומרה). עם זאת, התכונה הזו לא זמינה כרגע ל-Fragments. פריימים איטיים או קפואים של Fragment מוגדרים כפריימים איטיים או קפואים של כל הפעילות בין הקריאות החוזרות (callback) של onFragmentAttached() ו-onFragmentDetached() במחזור החיים של ה-Fragment.

בהשראת המחלקה AppStateMonitor (שהיא חלק מ-Performance Monitoring SDK שאחראי על תיעוד של עקבות מסך לפעילות), הטמענו את המחלקה ScreenTrace (שהיא חלק ממאגר קוד המקור של ה-Codelab הזה). אפשר לקשר את המחלקה ScreenTrace לקריאה החוזרת של מחזור החיים של FragmentManager כדי לצלם פריימים איטיים או קפואים. הכיתה הזו מספקת שני ממשקי API ציבוריים:

  • recordScreenTrace(): מתחילים להקליט את המסך
  • sendScreenTrace(): מפסיק את ההקלטה של מעקב אחר מסך ומצרף מדדים מותאמים אישית ליומן של ספירת המסגרות הכוללת, האיטית והקפואה

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

כך מתעדים את המסך עבור Fragment:

  1. מאתחלים את המחלקה ScreenTrace בפעילות שמארחת את ה-Fragment.

MainActivity.java

// Declare the Fragment tag
private static final String FRAGMENT_TAG = TestFragment.class.getSimpleName();

// TODO (1): Declare the ScreenTrace variable.
private ScreenTrace screenTrace;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // TODO (2): Initialize the ScreenTrace variable.
    screenTrace = new ScreenTrace(this, FRAGMENT_TAG);

    // ...
}
  1. כשמטעינים את ה-Fragment, נרשמים ל-FragmentLifecycleCallbacks ומבטלים את ההגדרה של קריאות החזרה onFragmentAttached() ו-onFragmentDetached(). אנחנו כבר עשינו את זה בשבילך. צריך להתחיל להקליט את עקבות המסך בפונקציית הקריאה החוזרת onFragmentAttached() ולהפסיק את ההקלטה בפונקציית הקריאה החוזרת onFragmentDetached().

MainActivity.java

private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
       new FragmentManager.FragmentLifecycleCallbacks() {

           @Override
           public void onFragmentAttached(@NonNull FragmentManager fm, @NonNull Fragment f, @NonNull Context context) {
               super.onFragmentAttached(fm, f, context);

               // TODO (3): Start recording the screen traces as soon as the Fragment is
               //  attached to its host Activity.
               if (FRAGMENT_TAG.equals(f.getTag()) && screenTrace.isScreenTraceSupported()) {
                   screenTrace.recordScreenTrace();
               }
           }

           @Override
           public void onFragmentDetached(@NonNull FragmentManager fm, @NonNull Fragment f) {
               super.onFragmentDetached(fm, f);

               // TODO (4): Stop recording the screen traces as soon as the Fragment is
               //  detached from its host Activity.
               if (FRAGMENT_TAG.equals(f.getTag()) && screenTrace.isScreenTraceSupported()) {
                   screenTrace.sendScreenTrace();
               }

               // Unregister Fragment lifecycle callbacks after the Fragment is detached
               fm.unregisterFragmentLifecycleCallbacks(fragmentLifecycleCallbacks);
           }
       };
  1. מפעילים מחדש את האפליקציה ומקישים על הלחצן LOAD FRAGMENT. ממתינים כמה שניות ואז לוחצים על הסמל back button בסרגל הניווט התחתון.

מסננים את ה-logcat לפי מדד מעקב של רישום ביומן ומחפשים יומנים כמו אלה שמופיעים בהמשך:

I/FirebasePerformance: Logging trace metric: _st_MainActivity-TestFragment (duration: XXXms)

מסננים את logcat באמצעות FireperfViews, ואז מחפשים יומנים כמו אלה שבהמשך:

D/FireperfViews: sendScreenTrace MainActivity-TestFragment, name: _st_MainActivity-TestFragment, total_frames: XX, slow_frames: XX, frozen_frames: XX

‫🎉 איזה כיף! הצלחתם למדוד את המסגרות האיטיות או הקפואות של Fragment ולדווח על הנתונים האלה ל-Firebase Performance Monitoring. בהמשך ה-codelab נראה את המדדים שתועדו במסוף Firebase.

9. בדיקת מדדים במסוף Firebase

  1. ב-logcat, לוחצים על כתובת ה-URL של מסוף Firebase כדי לעבור לדף הפרטים של מעקב. ceb9d5ba51bb6e89.jpeg

אפשרות נוספת: במסוף Firebase, בוחרים את הפרויקט שכולל את האפליקציה. בחלונית הימנית, מאתרים את הקטע Release & Monitor (השקה ומעקב) ולוחצים על Performance (ביצועים).

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

a0d8455c5269a590.png

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

5e92a307b7410d8b.png

  1. אפשר גם לראות את נתוני הביצועים של מעקב המסך המותאם אישית.
  • חוזרים לכרטיסייה הראשית לוח בקרה, גוללים למטה לטבלת העקבות ולוחצים על הכרטיסייה Screen rendering (עיבוד המסך). בטבלה הזו יופיעו עקבות המסך המותאמים אישית שהוספנו קודם, וגם עקבות מסך מוכנים לשימוש, כמו MainActivity trace.
  • מחפשים את עקבות המסך המותאמות אישית, MainActivity-TestFragment. לוחצים על שם העקבות כדי לראות את הנתונים המצטברים של עיבוד איטי ופריז של פריימים.

ee7890c7e2c28740.png

10. מזל טוב

כל הכבוד! הצלחתם למדוד את זמן הטעינה ואת ביצועי העיבוד של מסך של Activity ושל Fragment באמצעות Firebase Performance Monitoring.

מה השגתם

המאמרים הבאים

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

בנוסף, Firebase Performance מציע מעקב אוטומטי אחר בקשות רשת HTTP/S. כך תוכלו להגדיר בקלות בקשות רשת בלי לכתוב שורת קוד אחת. אפשר לנסות לשלוח כמה בקשות לרשת מהאפליקציה ולחפש את המדדים במסוף Firebase?

בונוס

עכשיו כשאתה יודע איך למדוד את זמן הטעינה ואת ביצועי העיבוד של המסך של הפעילות או של קטע הקוד באמצעות מעקב אחר קוד בהתאמה אישית, האם תוכל לעיין בבסיס הקוד שלנו עם קוד פתוח כדי לראות אם אפשר לתעד את המדדים האלה מחוץ לקופסה לכל פעילות או קטע קוד שמהווים חלק מהאפליקציה? אפשר לשלוח את ה-PR אם רוצים :-)

11. למידה נוספת

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

cd61c1495fad7961.png