Unter Android 16 (API-Level 36) und höher können Sie mit ProfilingManager Profile automatisch als Reaktion auf Systemtrigger erfassen, z. B. ANR-Fehler (App antwortet nicht), Abstürze aufgrund von Speichermangel (OutOfMemoryError) oder App-Start-Ereignisse. Da Sie nicht vorhersagen können, wann viele dieser Ereignisse im Feld auftreten, ist es unpraktisch, eine Aufzeichnung im Voraus manuell zu starten. Bei der triggerbasierten Profilerstellung verwaltet das System die Erfassung und liefert das resultierende Profil an Ihre Anwendung, sodass Sie Folgendes tun können:
- Stabilitätsprobleme beheben: Unvorhersehbare ANRs und Speicherlecks diagnostizieren.
- Critical User Journeys (CUJs) optimieren: Analysieren Sie Abläufe wie den App-Start (
TRIGGER_TYPE_COLD_STARTundTRIGGER_TYPE_APP_FULLY_DRAWN). - Nutzerverhalten analysieren: Sie können Ereignisse wie von Nutzern initiierte App-Beendigungen prüfen.
Eine vollständige Liste der verfügbaren Trigger finden Sie unter ProfilingTrigger.
Arten von Triggern
Trigger unterscheiden sich darin, wie das System das Profil erfasst, wenn das Ereignis eintritt.
Trigger, die den Hintergrund-Trace verwenden
Viele Auslöser, z. B. ein ANR-Fehler, sind die Folge eines früheren Problems. Wenn Sie einen Trace erst starten, nachdem das System den ANR-Fehler erkannt hat, wird die Blockierung, die ihn verursacht hat, nicht berücksichtigt. Um die Aktivität vor dem Ereignis zu erfassen, speichern die folgenden Trigger einen Snapshot des aktiven Hintergrund-Trace des Systems. Wenn beim Auftreten des Ereignisses kein Hintergrund-Trace ausgeführt wird, wird kein Profil erfasst:
TRIGGER_TYPE_ANR(API-Level 36)TRIGGER_TYPE_APP_FULLY_DRAWN(API-Level 36)TRIGGER_TYPE_APP_REQUEST_RUNNING_TRACE(API-Level 36.1)TRIGGER_TYPE_KILL_FORCE_STOP(API-Level 36.1)TRIGGER_TYPE_KILL_RECENTS(API-Level 36.1)TRIGGER_TYPE_KILL_TASK_MANAGER(API-Level 36.1)TRIGGER_TYPE_KILL_EXCESSIVE_CPU_USAGE(API-Level 37)
Trigger, die eine neue Erfassung starten
Die folgenden Trigger hängen nicht vom Hintergrund-Trace ab. Stattdessen startet das System eine neue Profilerfassung, wenn das Ereignis auftritt:
TRIGGER_TYPE_COLD_START(API-Level 37)TRIGGER_TYPE_OOM(API-Level 37)
Auslöser, bei denen das Erfassungsverhalten je nach Anomalie variiert
Bei den folgenden Anomalieauslösern hängen der erfasste Profiltyp (Speichern eines Snapshots des aktiven Hintergrund-Trace oder Starten einer neuen Erfassung) und das Verhalten der Ratenbegrenzung von der spezifischen Anomalie ab, die vom System erkannt wurde:
TRIGGER_TYPE_ANOMALY(API-Level 37)TRIGGER_TYPE_APP_COMPAT(API-Level 37)
Trigger einrichten
Der folgende Code zeigt, wie Sie sich für den Trigger TRIGGER_TYPE_APP_FULLY_DRAWN registrieren und eine Wartezeit dafür festlegen.
Kotlin
@RequiresApi(Build.VERSION_CODES.BAKLAVA) fun recordWithTrigger() { val profilingManager = applicationContext.getSystemService(ProfilingManager::class.java) ?: return val triggers = ArrayList<ProfilingTrigger>() val triggerBuilder = ProfilingTrigger.Builder(ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN) .setRateLimitingPeriodHours(1) triggers.add(triggerBuilder.build()) val callbackExecutor: 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(callbackExecutor, resultCallback) profilingManager.addProfilingTriggers(triggers) Choreographer.getInstance().postFrameCallback { frameTimeNanos -> // This will cause the TRIGGER_TYPE_APP_FULLY_DRAWN to be emitted. reportFullyDrawn() } }
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 callbackExecutor = 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(callbackExecutor, resultCallback); profilingManager.addProfilingTriggers(triggers); Choreographer.getInstance().postFrameCallback((f) -> { // This will cause the TRIGGER_TYPE_APP_FULLY_DRAWN to be emitted. reportFullyDrawn(); }); }
Der Code führt folgende Schritte aus:
- Manager abrufen: Ruft den
ProfilingManager-Dienst ab. - Trigger definieren: Erstellt einen
ProfilingTriggerfürTRIGGER_TYPE_APP_FULLY_DRAWN. Dieses Ereignis tritt auf, wenn die App während eines aktivitätsinitiierten KaltstartsreportFullyDrawn()aufruft, um zu melden, dass sie gestartet wurde und interaktiv ist. - Trigger-Cooldown festlegen: Wendet zusätzlich zur System-Ratenbegrenzung eine einstündige Wartefrist auf diesen bestimmten Trigger an, indem
setRateLimitingPeriodHoursverwendet wird. Diese Wartezeit verhindert, dass die App mehr als ein Startprofil pro Stunde aufzeichnet. - Listener registrieren: Ruft
registerForAllProfilingResultsauf, um den Callback zu definieren, der das Ergebnis verarbeitet. Dieser Callback empfängt den Pfad des gespeicherten Profils übergetResultFilePath(). - Trigger hinzufügen: Registriert die Triggerliste im
ProfilingManagerüberaddProfilingTriggers. - Ereignis auslösen: Postet einen
Choreographer-Frame-Callback, derreportFullyDrawn()aufruft, sobald der nächste Frame gezeichnet wird. Dadurch wird das EreignisTRIGGER_TYPE_APP_FULLY_DRAWNan das System gesendet, wodurch das Erfassen von Profilen ausgelöst wird, sofern ein System-Hintergrund-Trace ausgeführt wurde und ein Kontingent für die Ratenbegrenzung verfügbar ist. Damit dieser Trigger ausgelöst wird, muss Ihre AppreportFullyDrawn()aufrufen.
Lebenszyklus von Triggern
Registrierte Trigger bleiben auch nach einem Neustart der App oder des Systems erhalten. Sie müssen Trigger nicht bei jedem Start Ihrer App oder nach einem Systemneustart neu registrieren. Sobald Sie Trigger mit addProfilingTriggers registriert haben, werden sie vom System automatisch für Ihre App verwaltet, bis Sie sie entfernen oder die App deinstalliert wird.
Wann die Registrierung von Triggern aufgehoben werden sollte
Da registrierte Trigger auch nach einem Neustart der App oder des Systems erhalten bleiben, werden Trigger, die in einer vorherigen Sitzung registriert wurden, nicht gestoppt, wenn addProfilingTriggers bei nachfolgenden Starts übersprungen wird. Wenn Sie die Erfassung von Profilen für bestimmte Ereignisse oder für alle Ereignisse beenden möchten, rufen Sie explizit removeProfilingTriggersByType oder clearProfilingTriggers auf.
In den folgenden Situationen werden Trigger abgemeldet:
- Targeting von Kohorten oder Rollback von Remote-Flags: Sie können die Profilierung über Kohorten hinweg mithilfe von Remote-Konfiguration oder A/B-Test-Flags segmentieren. Wenn ein Gerät die Stichprobenkohorte verlässt oder ein Flag deaktiviert wird, melden Sie die Trigger ab.
- Debugging-Kampagne beenden: Sobald Sie genügend Profile erfasst haben, um ein Problem zu diagnostizieren oder eine Fehlerbehebung bereitzustellen, entfernen Sie den Trigger. Wenn Sie nicht benötigte Trigger abmelden, sparen Sie Gerätespeicher und Kontingent für die Ratenbegrenzung Ihrer App für andere Profilerstellungsanfragen.
Callback-Lebenszyklus
Im Gegensatz zu direkten Profilerstellungsanfragen liefern triggerbasierte Profile nur Ergebnisse an einen globalen Listener, der bei registerForAllProfilingResults registriert ist. Rufen Sie in Ihrem Callback ProfilingResult.getTriggerType auf, um zu ermitteln, welcher Trigger die Erfassung initiiert hat, und rufen Sie ProfilingResult.getResultFilePath auf, um die gespeicherte Profildatei zu finden. Weitere Informationen zu Dateinamen von Triggerprofilen, zum Abrufen von Dateien mit adb und zum Hochladen von Ergebnissen im Hintergrund finden Sie unter Profile abrufen und hochladen.
Trigger bleiben auch nach einem Neustart der App oder des Systems erhalten, der Ergebnis-Callback jedoch nicht. Registrieren Sie Ihren Callback in einem Hintergrund-Executor, nachdem die App gestartet wurde, damit der Kaltstart nicht verlangsamt wird. So erhalten Sie Profile in den beiden folgenden Szenarien:
- Während Ihre App ausgeführt wird: Bei Triggern, die Ihre App nicht beenden oder zum Absturz bringen, ruft das System Ihren Callback im angegebenen
Executorauf, wenn der Trigger auftritt und das Profil gespeichert wird. Nach dem Beenden der App: Bei Triggern, die häufig mit dem Beenden der App verbunden sind oder diesem vorausgehen (z. B.
TRIGGER_TYPE_ANR,TRIGGER_TYPE_OOModerTRIGGER_TYPE_KILL_RECENTS), oder wenn Ihre App vor dem Abschluss der Erfassung beendet wird, speichert das System das Profil im Hintergrund und stellt das Ergebnis in die Warteschlange, damit es nach einem Neustart der App oder des Systems verfügbar ist. Das System speichert nicht zugestellte Ergebnisse in der Warteschlange bis zu 7 Tage lang und stellt sie beim nächsten Ausführen der App und Aufrufen vonregisterForAllProfilingResultszu.
So funktioniert die Hintergrundverfolgung
Um den Profilerstellungsaufwand mit der Erfassung nützlicher Artefakte in Einklang zu bringen, führt das System Hintergrund-Traces nicht kontinuierlich aus. Stattdessen werden regelmäßig Stichproben genommen, indem Hintergrund-Traces in zufälligen Intervallen gestartet werden.
Vom System ausgelöste Hintergrund-Traces verwenden einen Ringpuffer mit einer systemdefinierten maximalen Größe. Wenn der Zwischenspeicher voll ist, werden die ältesten Daten durch neue Trace-Daten überschrieben. Wie in Abbildung 1 dargestellt, speichert das System den Ringpuffer-Snapshot mit den letzten Aktivitäten, die zum Trigger geführt haben, wenn ein registrierter Trigger ausgelöst wird, während ein Hintergrund-Trace aktiv ist und ein Kontingent für die Ratenbegrenzung verfügbar ist.
Auslöserspezifische Wartezeit festlegen
Bei Triggern mit hoher Häufigkeit kann das Kontingent für die Ratenbegrenzung Ihrer App schnell aufgebraucht sein.
Um zu verhindern, dass ein einzelner Triggertyp Ihr Kontingent aufbraucht, können Sie mit setRateLimitingPeriodHours zusätzlich zur systemweiten Ratenbegrenzung eine Mindestwartezeit pro Trigger hinzufügen.
Trigger lokal debuggen
Da Hintergrund-Traces in der Produktion in zufälligen Intervallen ausgeführt werden, müssen Sie zum lokalen Testen von Triggern einen kontinuierlichen Hintergrund-Trace erzwingen und die Ratenbegrenzung auf Systemebene für Ihr Paket umgehen. Alle pro Trigger festgelegten Cooldowns mit setRateLimitingPeriodHours gelten weiterhin und rate_limiter.disabled gilt nur für App-gesteuerte requestProfiling-Aufrufe.
Informationen zu den adb-Befehlen, mit denen Sie Hintergrund-Traces erzwingen, Trigger über die Befehlszeile simulieren und den Dienststatus prüfen können, finden Sie unter Hintergrund-Traces für Triggertests erzwingen in Debugging-Befehle für lokales Profiling.