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
פרטים על הצגת הנתונים האלה זמינים במאמר אחזור וניתוח של נתוני פרופילים.
איך פועל מעקב ברקע
כדי לתעד נתונים מלפני הפעלת הטריגר, מערכת ההפעלה מתחילה מעקב ברקע באופן תקופתי. אם מתרחש טריגר בזמן שהמעקב ברקע פעיל והאפליקציה רשומה עבורו, המערכת שומרת את פרופיל המעקב בספרייה של האפליקציה. לאחר מכן, הפרופיל יכלול מידע שהוביל להפעלת הטריגר.
אחרי ששומרים את הפרופיל, המערכת שולחת לאפליקציה התראה באמצעות פונקציית ה-callback שסופקה ל-registerForAllProfilingResults. הקריאה החוזרת הזו מספקת את הנתיב לפרופיל שצולם, שאפשר לגשת אליו באמצעות קריאה ל-ProfilingResult#getResultFilePath().
כדי לצמצם את ההשפעה על ביצועי המכשיר ועל חיי הסוללה, המערכת לא מפעילה את המעקב ברקע באופן רציף. במקום זאת, הוא משתמש בשיטת דגימה. המערכת מתחילה באופן אקראי מעקב ברקע בתוך מסגרת זמן מוגדרת (עם משך מינימלי ומקסימלי). הוספת רווחים אקראיים בין העקבות האלה משפרת את הכיסוי של הטריגרים.
לפרופילים שמופעלים על ידי המערכת יש גודל מקסימלי שמוגדר על ידי המערכת, ולכן הם משתמשים במאגר נתונים זמני מעגלי. כשהמאגר מלא, נתוני מעקב חדשים מחליפים את הנתונים הכי ישנים. כפי שמוצג באיור 1, יכול להיות שהמעקב שנתפס לא יכסה את כל משך ההקלטה ברקע אם המאגר יתמלא. במקום זאת, הוא מייצג את הפעילות האחרונה שהובילה להפעלה.
הטמעה של הגבלת קצב של יצירת בקשות ספציפית לטריגר
טריגרים בתדירות גבוהה יכולים לנצל במהירות את מכסת מגביל הקצב של האפליקציה. כדי להבין טוב יותר את מגביל הקצב, מומלץ לעיין במאמר איך מגביל הקצב פועל. כדי למנוע מצב שבו סוג טריגר ינצל את כל המכסה, אפשר להגדיר הגבלת קצב ספציפית לטריגר.
ProfilingManager תומך בהגבלת קצב ספציפית לטריגר שהוגדרה על ידי האפליקציה. כך תוכלו להוסיף עוד שכבה של הגבלת קצב על בסיס זמן, בנוסף למגביל הקצב הקיים. משתמשים ב-API setRateLimitingPeriodHours כדי להגדיר זמן השהיה ספציפי להפעלת טריגר. אחרי שתקופת ההמתנה תסתיים, תוכלו להפעיל אותו שוב.
ניפוי באגים בטריגרים באופן מקומי
קשה לבצע ניפוי באגים בהפעלות של טריגרים באופן מקומי, כי ההפעלות של טרייסים ברקע מתבצעות בזמנים אקראיים. כדי לכפות טרייס ברקע לצורך בדיקה, משתמשים בפקודת ה-ADB הבאה:
adb shell device_config put profiling_testing system_triggered_profiling.testing_package_name <com.example.myapp>
הפקודה הזו מאלצת את המערכת להתחיל מעקב רציף ברקע של החבילה שצוינה, כך שכל טריגר יוכל לאסוף פרופיל אם מגביל הקצב מאפשר זאת.
אפשר גם להפעיל אפשרויות אחרות לניפוי באגים, למשל השבתה של מגביל הקצב כשמבצעים ניפוי באגים באופן מקומי. מידע נוסף זמין במאמר בנושא פקודות לניפוי באגים בפרופילים מקומיים.