שימוש ב-ViewCapture באפליקציות מערכת

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

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

בדף הזה נסביר איך להטמיע את ViewCapture באפליקציות מערכת.

שימוש

ViewCapture.java מטמיע מופע של onDrawListener וצובר נתיב של ViewCapture במהלך תהליך הציור. כל ציור מחדש של מסגרת מפעיל סריקה של היררכיית עץ התצוגה, החל מתצוגת הבסיס של החלון. כדי לשפר את הביצועים, ב-ViewCapture נעשה שימוש בשיטות getter ציבוריות של View.java כדי לאחזר ולהעתיק ערכים לשרשור רקע. ההטמעה של ViewCapture מבצעת אופטימיזציה של התהליך הזה על ידי בדיקה אם תצוגה (view) לא עדכנית או לא חוקית באמצעות [captureViewTree](https://cs.android.com/android/platform/superproject/+/android-latest-release:frameworks/libs/systemui/viewcapturelib/src/com/android/app/viewcapture/ViewCapture.java?q=%22captureViewTree(View%22), וכך נמנעת סריקה של כל היררכיית התצוגות. captureViewTree זמין רק לאפליקציות מערכת והוא חלק מ-UnsupportedAppUsage API. השימוש ב-API הזה מוגבל לאפליקציות על סמך גרסת ה-SDK היעד שלהן.

מגבלות

בקטעים הבאים מתוארות המגבלות של הביצועים והזיכרון בזמן הרצת ViewCapture.

ביצועים

העלות הנוספת הממוצעת של הליבה הראשית לביצועים של ViewCapture היא 195 μs. עם זאת, בתרחישים הגרועים ביותר, התהליך עשוי להימשך כ-5 אלפיות השנייה. אפשר לעיין בפלחים vc#onDraw ב-trace של Perfetto.

העלויות הכלליות נובעות בעיקר מהפעולות הבאות:

  1. זמן הסריקה של ההיררכיה הוא 50 μs, גם אחרי גיזום.
  2. אחסון עותקים של מאפייני תצוגה מפורטת על ידי אחזור אובייקטים ממקצה של רשימת פריטים פנויים עולה 20 מיקרו-שניות.
  3. אחזור של כל ערך נכס באמצעות פונקציית getter גורם להרבה קריאות נוספות לפונקציה בכל תצוגה, שעולות 110 מיקרו-שניות.

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

זיכרון

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

הוראות

כדי להטמיע את ViewCapture באפליקציות מערכת:

  1. מוסיפים את התלות לקובץ Android.bp, כפי שמתואר בקוד של מרכז האפליקציות.

    android_library {
        name: "YourLib",
        static_libs: [
              ...
            "//frameworks/libs/systemui:view_capture",
              ...
        ],
        platform_apis: true,
        privileged: true,
    }
    
  2. יוצרים מופע של ViewCapture כשיוצרים את החלון, לדוגמה:

    • דוגמה 1:

      private SafeCloseable mViewCapture;
      
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        ...
        mViewCapture = ViewCaptureFactory.getInstance(this).startCapture(getWindow());
      }
      
    • דוגמה 2:

      private SafeCloseable mViewCapture;
      
      @Override
      protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (enableViewCaptureTracing()) {
            mViewCaptureCloseable = ViewCaptureFactory.getInstance(getContext())
              .startCapture(getRootView(), ".NotificationShadeWindowView");
        }
        ...
      }
      
  3. סוגרים את המכונה של ViewCapture כשמשמידים את החלון, כפי שמתואר בדוגמאות הבאות:

    • דוגמה 1:

      @Override
      public void onDestroy() {
        ...
        if (mViewCapture != null) mViewCapture.close();
      }
      
    • דוגמה 2:

      @Override
      protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mViewCaptureCloseable != null) {
            mViewCaptureCloseable.close();
       }
        ...
      }