Auf dieser Seite wird beschrieben, wie Sie mit der ProfilingManager API einen System-Trace aufzeichnen.
ProfilingManager kann auch andere Profiltypen aufzeichnen. Dieser Prozess ähnelt dem Aufzeichnen eines System-Traces, aber für jeden Typ wird ein anderer Builder verwendet. Die unterstützten Profile und ihre Builder sind:
System-Traces:Werden mit
SystemTraceRequestBuilderaufgezeichnet und sind nützlich für die Latenzanalyse und das allgemeine Debugging der Leistung.Heap-Dumps:Mit
JavaHeapDumpRequestBuilderaufgezeichnete Daten, die bei der Erkennung und Optimierung von Speicherlecks hilfreich sind.Heap-Profile:Mit
HeapProfileRequestBuilderaufgezeichnet, was für die Speicheroptimierung nützlich ist.Aufrufstack-Profile:Werden mit
StackSamplingRequestBuilderaufgezeichnet und sind nützlich, um die Code-Ausführung und Latenzanalyse zu verstehen.
Abhängigkeiten hinzufügen
Für eine optimale Nutzung der ProfilingManager API fügen Sie die folgenden Jetpack-Bibliotheken in Ihre build.gradle.kts-Datei ein.
Kotlin
dependencies { implementation("androidx.tracing:tracing-ktx:2.0.2") implementation("androidx.core:core:1.19.0") }
Groovy
dependencies { implementation 'androidx.tracing:tracing:2.0.2' implementation 'androidx.core:core:1.19.0' }
System-Trace aufzeichnen
Nachdem Sie die erforderlichen Abhängigkeiten hinzugefügt haben, können Sie mit dem folgenden Code einen System-Trace aufzeichnen. In diesem Beispiel wird gezeigt, wie Sie eine Profilerstellungssitzung über eine Composable-Funktion starten und gleichzeitig rechenintensive Vorgänge sicher vom Hauptthread ausführen.
Kotlin
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
@Composable
fun ProfiledScreen(modifier: Modifier = Modifier) {
// Use the application context: requestProfiling resolves the ProfilingManager
// system service from it, so there's no reason to hand it a short-lived Activity.
val appContext = LocalContext.current.applicationContext
val scope = rememberCoroutineScope()
Button(
onClick = {
// Run the orchestration off the main thread. Profiling a heavy operation
// on the UI thread would freeze the UI (ANR) and distort the very metrics
// you're trying to capture.
//
// Note: this scope is tied to composition. If the user leaves this screen
// mid-session, the coroutine is cancelled and stopSignal.cancel() might not
// run, but setDurationMs() acts as a safety net and ends the trace.
scope.launch(Dispatchers.Default) {
val callbackExecutor = Dispatchers.IO.asExecutor()
val resultCallback = Consumer<ProfilingResult> { profilingResult ->
if (profilingResult.errorCode == ProfilingResult.ERROR_NONE) {
Log.d("ProfileTest", "Result file: ${profilingResult.resultFilePath}")
} else {
// errorMessage explains the failure (e.g., rate limiting); keep it.
Log.e(
"ProfileTest",
"Profiling failed errorCode=${profilingResult.errorCode} " +
"errorMessage=${profilingResult.errorMessage}"
)
}
}
val stopSignal = CancellationSignal()
val requestBuilder = SystemTraceRequestBuilder().apply {
setCancellationSignal(stopSignal)
setTag("FOO") // Caller-supplied tag for identification.
setDurationMs(60000) // Hard cap: ends the session if cancel() never fires.
setBufferFillPolicy(BufferFillPolicy.RING_BUFFER)
setBufferSizeKb(32768)
}
// 1. Start the session. This is asynchronous system IPC. The tracing
// engine takes a moment to start and allocate buffers.
requestProfiling(appContext, requestBuilder.build(), callbackExecutor, resultCallback)
// 2. The API exposes no "profiling started" signal, so pad with a short,
// best-effort delay before running the code you care about. This is
// approximate. Increase it on slower or heavily loaded devices.
delay(STARTUP_PADDING_MS)
// 3. The session is already recording every thread in your app. This slice
// doesn't scope what's captured. It just labels this region of the
// timeline so heavyOperation() is easier to find. trace { } closes the
// section even if the block throws.
trace("MyApp:HeavyOperation") {
heavyOperation()
}
// 4. Stop recording. Until this fires or the setDurationMs() cap is
// reached (whichever comes first), the session keeps capturing app-wide
// activity.
stopSignal.cancel()
}
}
) {
Text("Run & Profile Heavy Operation")
}
}
// Best-effort wait for the system trace engine to initialize before profiling.
// There is no deterministic start callback; tune this for your target devices.
private const val STARTUP_PADDING_MS = 100L
fun heavyOperation() {
// Background computations to profile.
}
Java
void heavyOperation() {
// Computations you want to profile
}
void sampleRecordSystemTrace() {
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());
}
}
};
CancellationSignal stopSignal = new CancellationSignal();
SystemTraceRequestBuilder requestBuilder = new SystemTraceRequestBuilder();
requestBuilder.setCancellationSignal(stopSignal);
requestBuilder.setTag("FOO");
requestBuilder.setDurationMs(60000);
requestBuilder.setBufferFillPolicy(BufferFillPolicy.RING_BUFFER);
requestBuilder.setBufferSizeKb(32768);
Profiling.requestProfiling(getApplicationContext(), requestBuilder.build(), mainExecutor,
resultCallback);
// Wait some time for profiling to start.
Trace.beginSection("MyApp:HeavyOperation");
heavyOperation();
Trace.endSection();
// Once the interesting code section is profiled, stop profile
stopSignal.cancel();
}
Im Beispielcode wird die Profilerstellungssitzung durch die folgenden Schritte eingerichtet und verwaltet:
Executor einrichten: Erstellen Sie eine
Executor, um den Thread zu definieren, der die Profiling-Ergebnisse empfängt. Das Profiling erfolgt im Hintergrund. Wenn Sie einen Executor für einen Thread verwenden, der nicht der UI-Thread ist, können Sie Fehler vom Typ „App antwortet nicht“ (ANR) vermeiden, falls Sie dem Callback später weitere Verarbeitung hinzufügen.Profiler-Ergebnisse verarbeiten Erstellen Sie ein
Consumer<ProfilingResult>-Objekt. Das System verwendet dieses Objekt, um Profilergebnisse vonProfilingManageran Ihre App zurückzusenden.Erstellen Sie die Profiling-Anfrage. Erstellen Sie eine
SystemTraceRequestBuilder, um die Profilerstellungssitzung einzurichten. Mit diesem Builder können Sie dieProfilingManager-Trace-Einstellungen anpassen. Das Anpassen des Builders ist optional. Andernfalls verwendet das System Standardeinstellungen.- Tag definieren Mit
setTag()können Sie dem Namen des Traces ein Tag hinzufügen. Mit diesem Tag können Sie den Trace identifizieren. - Optional: Dauer festlegen Mit
setDurationMs()können Sie angeben, wie lange das Profiling in Millisekunden dauern soll. Mit60000wird beispielsweise ein 60-Sekunden-Trace festgelegt. Die Aufzeichnung wird nach der angegebenen Dauer automatisch beendet, wennCancellationSignalnicht vorher ausgelöst wird. - Pufferrichtlinie auswählen Mit
setBufferFillPolicy()können Sie festlegen, wie Trace-Daten gespeichert werden.BufferFillPolicy.RING_BUFFERbedeutet, dass neue Daten die ältesten Daten überschreiben, wenn der Puffer voll ist. So wird ein kontinuierlicher Datensatz der letzten Aktivitäten erstellt. - Puffergröße festlegen Mit
setBufferSizeKb()können Sie eine Puffergröße für das Tracing angeben, mit der Sie die Größe der Ausgabetracedatei steuern können.
- Tag definieren Mit
Optional: Sitzungslebenszyklus verwalten Erstellen Sie ein
CancellationSignal. Mit diesem Objekt können Sie die Profilerstellungssitzung jederzeit beenden und so die Dauer genau steuern.Starten und Ergebnisse erhalten Wenn Sie
requestProfiling()anrufen, startetProfilingManagerim Hintergrund eine Profilerstellungssitzung. Nach dem Profiling wird dieProfilingResultan IhreresultCallback#accept-Methode gesendet. Wenn das Profiling erfolgreich abgeschlossen wurde, gibtProfilingResultden Pfad an, unter dem der Trace auf Ihrem Gerät überProfilingResult#getResultFilePathgespeichert wurde. Sie können diese Datei programmatisch oder für das lokale Profiling durch Ausführen vonadb pull <trace_path>auf Ihrem Computer abrufen.Benutzerdefinierte Trace-Punkte hinzufügen Sie können benutzerdefinierte Trace-Punkte im Code Ihrer App hinzufügen. Im vorherigen Codebeispiel wird mit dem Block
trace("MyApp:HeavyOperation") { ... }ein benutzerdefinierter Slice im generierten Profil erstellt.