ProfilingManager supporta l'acquisizione di profili basati su trigger di sistema. Il sistema gestisce il processo di registrazione e fornisce il profilo risultante alla tua app.
I trigger sono legati a eventi critici per le prestazioni. I profili registrati dal sistema forniscono informazioni di debug dettagliate per i percorsi utente critici (CUJ) associati a questi trigger.
Acquisire dati storici
Molti trigger richiedono l'analisi dei dati storici che hanno portato all'evento. Il trigger stesso è spesso una conseguenza di un problema piuttosto che la causa principale. Se avvii un profilo solo dopo l'attivazione, la causa principale potrebbe essere già persa.
Ad esempio, un'operazione a lunga esecuzione sul thread UI causa un errore L'applicazione non risponde (ANR). Quando il sistema rileva l'errore ANR e segnala l'app, l'operazione potrebbe essere terminata. L'avvio di un profilo in quel momento non tiene conto del lavoro di blocco effettivo.
Prevedere esattamente quando si verificano alcuni trigger non è fattibile, il che rende impossibile avviare manualmente un profilo in anticipo.
Perché utilizzare l'acquisizione basata su trigger?
Il motivo principale per utilizzare i trigger di profilazione è acquisire dati per eventi imprevedibili in cui è impossibile per un'app avviare la registrazione manualmente prima che si verifichino questi eventi. I trigger di profilazione possono essere utilizzati per:
- Esegui il debug dei problemi di prestazioni:diagnostica gli ANR, le perdite di memoria e altri problemi di stabilità.
- Ottimizza i percorsi utente critici:analizza e migliora i flussi, ad esempio l'avvio dell'app.
- Comprendere il comportamento degli utenti: ottieni informazioni sugli eventi, ad esempio le uscite dall'app avviate dagli utenti.
Configurare un trigger
Il seguente codice mostra come registrarsi per il trigger
TRIGGER_TYPE_APP_FULLY_DRAWN e applicare la limitazione della frequenza.
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);
Il codice esegue questi passaggi:
- Get the manager: recupera il servizio
ProfilingManager. - Definisci un trigger: crea un
ProfilingTriggerperTRIGGER_TYPE_APP_FULLY_DRAWN. Questo evento si verifica quando l'app segnala di aver completato l'avvio ed è interattiva. - Imposta limiti di frequenza: applica un limite di frequenza di 1 ora a questo trigger specifico
(
setRateLimitingPeriodHours(1)). In questo modo, l'app non registra più di un profilo di avvio all'ora. - Register listener: chiama
registerForAllProfilingResultsper definire il callback che gestisce il risultato. Questo callback riceve il percorso del profilo salvato tramitegetResultFilePath(). - Aggiungi trigger: registra l'elenco dei trigger con
ProfilingManagerutilizzandoaddProfilingTriggers. - Attiva evento: chiama
reportFullyDrawn(), che emette l'eventoTRIGGER_TYPE_APP_FULLY_DRAWNal sistema attivando una raccolta del profilo presupponendo che sia in esecuzione una traccia in background del sistema e che sia disponibile una quota di limitatore di frequenza. Questo passaggio facoltativo mostra un flusso end-to-end perché la tua app deve chiamarereportFullyDrawn()per questo trigger.
Recuperare la traccia
Il sistema salva i profili basati sui trigger nella stessa directory degli altri profili. Il nome file per le tracce attivate segue questo formato:
profile_trigger_<profile_type_code>_<datetime>.<profile-type-name>
Puoi estrarre il file utilizzando ADB. Ad esempio, per estrarre la traccia di sistema acquisita con l'esempio di codice utilizzando ADB, potrebbe avere il seguente aspetto:
adb pull /data/user/0/com.example.sampleapp/files/profiling/profile_trigger_1_2025-05-06-14-12-40.perfetto-trace
Per informazioni dettagliate sulla visualizzazione di queste tracce, consulta Recuperare e analizzare i dati di profilazione.
Come funziona il tracciamento in background
Per acquisire i dati precedenti a un trigger, il sistema operativo avvia periodicamente una traccia in background. Se si verifica un trigger mentre questa traccia in background è attiva e la tua app è registrata per questo trigger, il sistema salva il profilo della traccia nella directory dell'app. Il profilo includerà quindi le informazioni che hanno portato all'attivazione.
Una volta salvato il profilo, il sistema invia una notifica alla tua app utilizzando il callback
fornito a registerForAllProfilingResults. Questo callback fornisce il percorso del profilo acquisito a cui è possibile accedere chiamando ProfilingResult#getResultFilePath().
Per ridurre l'impatto sulle prestazioni del dispositivo e sulla durata della batteria, il sistema non esegue le tracce in background in modo continuo. Utilizza invece un metodo di campionamento. Il sistema avvia in modo casuale una traccia in background entro un periodo di tempo prestabilito (con una durata minima e massima). La spaziatura casuale di queste tracce migliora la copertura del trigger.
I profili attivati dal sistema hanno una dimensione massima definita dal sistema, quindi utilizzano un buffer circolare. Una volta riempito il buffer, i nuovi dati di traccia sovrascrivono i dati meno recenti. Come mostrato nella Figura 1, una traccia acquisita potrebbe non coprire l'intera durata della registrazione in background se il buffer si riempie. Rappresenta invece l'attività più recente che ha portato all'attivazione.
Implementare la limitazione di frequenza specifica per l'attivatore
I trigger ad alta frequenza possono consumare rapidamente la quota del limitatore di frequenza della tua app. Per comprendere meglio il limitatore di frequenza, ti consigliamo di consultare Come funziona il limitatore di frequenza. Per evitare che un singolo tipo di trigger esaurisca la quota, puoi implementare la limitazione della frequenza specifica per il trigger.
ProfilingManager supporta la limitazione di frequenza specifica per i trigger definiti dalle app. In questo modo
puoi aggiungere un altro livello di limitazione basata sul tempo oltre al limitatore di velocità
esistente. Utilizza l'API setRateLimitingPeriodHours per impostare un tempo di raffreddamento specifico per un trigger. Al termine del periodo di raffreddamento, puoi attivarlo
di nuovo.
Eseguire il debug dei trigger in locale
Poiché le tracce in background vengono eseguite in momenti casuali, il debug dei trigger in locale è difficile. Per forzare una traccia in background per il test, utilizza il seguente comando ADB:
adb shell device_config put profiling_testing system_triggered_profiling.testing_package_name <com.example.myapp>
Questo comando forza il sistema ad avviare una traccia continua in background per il pacchetto specificato, consentendo a ogni trigger di raccogliere un profilo se il rate limiter lo consente.
Puoi anche attivare altre opzioni di debug, ad esempio disattivare il limitatore di frequenza durante il debug locale. Per ulteriori informazioni, vedi Comandi di debug per la profilazione locale.