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 che si è verificato il trigger, la causa principale potrebbe essere già persa.
Ad esempio, un'operazione a lunga esecuzione sul thread dell'interfaccia utente causa un errore 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 un'app non può avviare la registrazione manualmente prima che si verifichino questi eventi. I trigger di profilazione possono essere utilizzati per:
- Eseguire il debug dei problemi di prestazioni:diagnosticare errori ANR, perdite di memoria e altri problemi di stabilità.
- Ottimizzare i percorsi utente critici:analizzare e migliorare i flussi, ad esempio l'avvio dell'app.
- Comprendere il comportamento degli utenti:ottenere informazioni dettagliate sugli eventi, ad esempio le uscite dell'app avviate dall'utente.
Configurare un trigger
Il seguente codice mostra come registrarsi per il trigger TRIGGER_TYPE_APP_FULLY_DRAWN e applicare la limitazione di 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:
- Recuperare il gestore: recupera il servizio
ProfilingManager. - Definire un trigger: crea un
ProfilingTriggerperTRIGGER_TYPE_APP_FULLY_DRAWN. Questo evento si verifica quando l'app segnala di aver completato l'avvio ed è interattiva. - Impostare i 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. - Registrare il listener: chiama
registerForAllProfilingResultsper definire il callback che gestisce il risultato. Questo callback riceve il percorso del profilo salvato tramitegetResultFilePath(). - Aggiungere trigger: registra l'elenco dei trigger con
ProfilingManagerutilizzandoaddProfilingTriggers. - Attivare l'evento: chiama
reportFullyDrawn(), che genera l'eventoTRIGGER_TYPE_APP_FULLY_DRAWNnel sistema, attivando una raccolta di profili , presupponendo che sia in esecuzione una traccia in background del sistema e che sia disponibile la quota del limitatore di frequenza. Questo passaggio facoltativo mostra un flusso end-to-end perché l'app deve chiamarereportFullyDrawn()per questo trigger.
Recuperare la traccia
Il sistema salva i profili basati su trigger nella stessa directory degli altri profili. Il nome file delle 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 il codice di esempio utilizzando ADB, potrebbe essere simile a questa:
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 la traccia 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, il sistema salva il profilo della traccia nella directory dell'app. Il profilo includerà quindi le informazioni che hanno portato al trigger.
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 dei 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 più vecchi. 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 al trigger.
Implementare la limitazione di frequenza specifica per i trigger
I trigger ad alta frequenza possono consumare rapidamente la quota del limitatore di frequenza dell'app. Per comprendere meglio il limitatore di frequenza, ti invitiamo a consultare Come funziona il limitatore di frequenza. Per impedire che un singolo tipo di trigger esaurisca la quota, puoi implementare la limitazione di frequenza specifica per i trigger.
ProfilingManager supporta la limitazione di frequenza specifica per i trigger definita dall'app. In questo modo, puoi aggiungere un altro livello di limitazione basata sul tempo oltre al limitatore di frequenza esistente. Utilizza l'API setRateLimitingPeriodHours per impostare un tempo di assestamento specifico per un attivatore. Una volta scaduto il periodo di inattività, 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 i 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 in background continua per il pacchetto specificato, consentendo a ogni trigger di raccogliere un profilo se il limitatore di frequenza lo consente.
Puoi anche attivare altre opzioni di debug, ad esempio disattivare il limitatore di frequenza durante il debug in locale. Per ulteriori informazioni, consulta Comandi di debug per la profilazione locale.