ProfilingManager תומך בלכידת פרופילים על סמך טריגרים של המערכת. המערכת מנהלת את תהליך ההקלטה ומספקת את הפרופיל שנוצר לאפליקציה.
הטריגרים קשורים לאירועים קריטיים לביצועים. פרופילים שנרשמים על ידי המערכת מספקים מידע מפורט לניפוי באגים לגבי התהליכים הקריטיים שעוברים המשתמשים (CUJ) שמשויכים לטריגרים האלה.
איסוף נתונים היסטוריים
הרבה טריגרים דורשים ניתוח של הנתונים ההיסטוריים שהובילו לאירוע. הטריגר עצמו הוא לרוב תוצאה של בעיה ולא שורש הבעיה. אם מתחילים פרופיל רק אחרי שהטריגר מופעל, יכול להיות שכבר לא יהיה אפשר לגלות את שורש הבעיה.
לדוגמה, פעולה שפועלת לאורך זמן בשרשור ה-UI גורמת לשגיאה האפליקציה לא מגיבה (ANR). יכול להיות שהפעולה תסתיים לפני שהמערכת תזהה את שגיאת ה-ANR ותאותת לאפליקציה. אם מתחילים פרופיל באותו רגע, לא רואים את העבודה בפועל של החסימה.
אי אפשר לחזות בדיוק מתי יתרחשו טריגרים מסוימים, ולכן אי אפשר להפעיל פרופיל באופן ידני מראש.
למה כדאי להשתמש באיסוף נתונים שמבוסס על טריגרים?
הסיבה העיקרית לשימוש בטריגרים של פרופילים היא לכידת נתונים של אירועים בלתי צפויים, שאי אפשר להתחיל להקליט אותם באפליקציה באופן ידני לפני שהם מתרחשים. אפשר להשתמש במפעילים של יצירת פרופילים כדי:
- ניפוי באגים בבעיות בביצועים: אבחון של מקרי ANR, דליפות זיכרון ובעיות יציבות אחרות.
- אופטימיזציה של חוויות משתמשים הכרחיות: ניתוח ושיפור של תהליכים, למשל, הפעלת האפליקציה.
- הבנת התנהגות המשתמשים: קבלת תובנות לגבי אירועים, למשל, יציאות מהאפליקציה שהמשתמש יזם.
הגדרת טריגר
הקוד הבא מדגים איך להירשם לטריגר TRIGGER_TYPE_APP_FULLY_DRAWN ולהחיל עליו הגבלת קצב של יצירת בקשות.
Kotlin
fun recordWithTrigger() { val profilingManager = applicationContext.getSystemService(ProfilingManager::class.java) val triggers = ArrayList<ProfilingTrigger>() val triggerBuilder = ProfilingTrigger.Builder(ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN) .setRateLimitingPeriodHours(1) triggers.add(triggerBuilder.build()) val mainExecutor: Executor = Executors.newSingleThreadExecutor() val resultCallback = Consumer<ProfilingResult> { profilingResult -> if (profilingResult.errorCode == ProfilingResult.ERROR_NONE) { Log.d( "ProfileTest", "Received profiling result file=" + profilingResult.resultFilePath ) setupProfileUploadWorker(profilingResult.resultFilePath) } else { Log.e( "ProfileTest", "Profiling failed errorcode=" + profilingResult.errorCode + " errormsg=" + profilingResult.errorMessage ) } } profilingManager.registerForAllProfilingResults(mainExecutor, resultCallback) profilingManager.addProfilingTriggers(triggers)
Java
public void recordWithTrigger() { ProfilingManager profilingManager = getApplicationContext().getSystemService( ProfilingManager.class); List<ProfilingTrigger> triggers = new ArrayList<>(); ProfilingTrigger.Builder triggerBuilder = new ProfilingTrigger.Builder( ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN); triggerBuilder.setRateLimitingPeriodHours(1); triggers.add(triggerBuilder.build()); Executor mainExecutor = Executors.newSingleThreadExecutor(); Consumer<ProfilingResult> resultCallback = new Consumer<ProfilingResult>() { @Override public void accept(ProfilingResult profilingResult) { if (profilingResult.getErrorCode() == ProfilingResult.ERROR_NONE) { Log.d( "ProfileTest", "Received profiling result file=" + profilingResult.getResultFilePath()); setupProfileUploadWorker(profilingResult.getResultFilePath()); } else { Log.e( "ProfileTest", "Profiling failed errorcode=" + profilingResult.getErrorCode() + " errormsg=" + profilingResult.getErrorMessage()); } } }; profilingManager.registerForAllProfilingResults(mainExecutor, resultCallback); profilingManager.addProfilingTriggers(triggers);
הקוד מבצע את הפעולות הבאות:
- Get the manager: מאחזר את השירות
ProfilingManager. - הגדרת טריגר: יצירת
ProfilingTriggerעבורTRIGGER_TYPE_APP_FULLY_DRAWN. האירוע הזה מתרחש כשהאפליקציה מדווחת שהיא סיימה את ההפעלה ושהיא אינטראקטיבית. - הגדרת מגבלות קצב: מגדירה מגבלת קצב של שעה אחת לטריגר הספציפי הזה (
setRateLimitingPeriodHours(1)). כך האפליקציה לא תוכל להקליט יותר מפרופיל הפעלה אחד בשעה. - Register listener: קריאה ל-
registerForAllProfilingResultsכדי להגדיר את פונקציית הקריאה החוזרת שמטפלת בתוצאה. התקשרות חזרה הזו מקבלת את הנתיב של הפרופיל שנשמר דרךgetResultFilePath(). - Add triggers (הוספת טריגרים): רושם את רשימת הטריגרים ב-
ProfilingManagerבאמצעותaddProfilingTriggers. - Fire event: קורא ל-
reportFullyDrawn(), ששולח את האירועTRIGGER_TYPE_APP_FULLY_DRAWNלמערכת ומפעיל איסוף של פרופיל, בהנחה שפועל מעקב ברקע של המערכת ושיש מכסת מגבלות קצב זמינה. השלב האופציונלי הזה מדגים זרימת נתונים מקצה לקצה, כי האפליקציה שלכם צריכה לקרוא ל-reportFullyDrawn()בשביל הטריגר הזה.
אחזור התיעוד
המערכת שומרת פרופילים שמבוססים על טריגרים באותה ספרייה כמו פרופילים אחרים. שם הקובץ של מעקבים שהופעלו נקבע לפי הפורמט הבא:
profile_trigger_<profile_type_code>_<datetime>.<profile-type-name>
אפשר לשלוף את הקובץ באמצעות ADB. לדוגמה, כדי לשלוף את נתוני המעקב של המערכת שצולמו באמצעות הקוד לדוגמה באמצעות ADB, הקוד עשוי להיראות כך:
adb pull /data/user/0/com.example.sampleapp/files/profiling/profile_trigger_1_2025-05-06-14-12-40.perfetto-trace
פרטים על הצגת הנתונים האלה זמינים במאמר אחזור וניתוח של נתוני פרופילים.
איך פועל מעקב ברקע
כדי לתעד נתונים מלפני הפעלת הטריגר, מערכת ההפעלה מתחילה מעקב ברקע באופן תקופתי. אם מתרחש טריגר בזמן שהמעקב ברקע פעיל והאפליקציה רשומה עבורו, המערכת שומרת את פרופיל המעקב בספרייה של האפליקציה. הפרופיל יכלול מידע על מה שהוביל להפעלת הטריגר.
אחרי ששומרים את הפרופיל, המערכת שולחת לאפליקציה התראה באמצעות הקריאה החוזרת שצוינה ב-registerForAllProfilingResults. הקריאה החוזרת הזו מספקת את הנתיב לפרופיל שצולם, שאפשר לגשת אליו באמצעות הקריאה ל-ProfilingResult#getResultFilePath().
כדי לצמצם את ההשפעה על ביצועי המכשיר ועל חיי הסוללה, המערכת לא מפעילה את המעקב ברקע באופן רציף. במקום זאת, הוא משתמש בשיטת דגימה. המערכת מתחילה באופן אקראי מעקב ברקע בתוך מסגרת זמן מוגדרת (עם משך מינימלי ומקסימלי). הוספת רווחים אקראיים בין העקבות האלה משפרת את הכיסוי של הטריגרים.
לפרופילים שמופעלים על ידי המערכת יש גודל מקסימלי שמוגדר על ידי המערכת, ולכן הם משתמשים במאגר זמני (ring buffer). אחרי שהמאגר מלא, נתוני מעקב חדשים מחליפים את הנתונים הכי ישנים. כפי שמוצג באיור 1, יכול להיות שהתיעוד לא יכסה את כל משך ההקלטה ברקע אם המאגר יתמלא. במקום זאת, הוא יציג את הפעילות האחרונה שהובילה להפעלה.
הטמעה של הגבלת קצב של יצירת בקשות שספציפית לטריגר
טריגרים בתדירות גבוהה יכולים לנצל במהירות את מכסת מגביל הקצב של האפליקציה. כדי להבין טוב יותר את מגביל הקצב, מומלץ לעיין במאמר איך מגביל הקצב פועל. כדי למנוע מצב שבו סוג טריגר ינצל את כל המכסה, אפשר להגדיר הגבלת קצב ספציפית לטריגר.
ProfilingManager תומך בהגבלת קצב ספציפית לטריגר שהוגדרה על ידי האפליקציה. כך אפשר להוסיף עוד שכבת הגבלת קצב על בסיס זמן, בנוסף למגביל הקצב הקיים. משתמשים ב-API setRateLimitingPeriodHours כדי להגדיר זמן השהיה ספציפי להפעלת טריגר. אחרי שתקופת הצינון תסתיים, תוכלו להפעיל אותה שוב.
ניפוי באגים בטריגרים באופן מקומי
קשה לבצע ניפוי באגים בהפעלות של טריגרים באופן מקומי, כי הפעלות של מעקב אחר רקע מתבצעות בזמנים אקראיים. כדי לכפות מעקב ברקע לצורך בדיקה, משתמשים בפקודת ה-ADB הבאה:
adb shell device_config put profiling_testing system_triggered_profiling.testing_package_name <com.example.myapp>
הפקודה הזו מאלצת את המערכת להתחיל מעקב רציף ברקע של החבילה שצוינה, כך שכל טריגר יוכל לאסוף פרופיל אם מגביל הקצב מאפשר זאת.
אפשר גם להפעיל אפשרויות אחרות לניפוי באגים, למשל השבתה של מגביל הקצב כשמבצעים ניפוי באגים באופן מקומי. מידע נוסף זמין במאמר בנושא פקודות לניפוי באגים בפרופילים מקומיים.