Auf dieser Seite wird beschrieben, wie Sie mit der ProfilingManager API einen System-Trace aufzeichnen.
ProfilingManager kann auch andere Profiltypen aufzeichnen. Dieser Vorgang ähnelt dem Aufzeichnen eines System-Trace, 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 Leistungs-Debugging.Heap-Dumps: Werden mit
JavaHeapDumpRequestBuilderaufgezeichnet und sind hilfreich für die Erkennung und Optimierung von Speicherlecks.Heap-Profile: Werden mit
HeapProfileRequestBuilderaufgezeichnet und sind nützlich für die Speicheroptimierung.Aufrufstack-Profile: Werden mit
StackSamplingRequestBuilderaufgezeichnet und sind nützlich, um die Codeausführung und Latenzanalyse zu verstehen.
Abhängigkeiten hinzufügen
Für eine optimale Nutzung der ProfilingManager API fügen Sie der Datei build.gradle.kts die folgenden Jetpack-Bibliotheken hinzu.
Kotlin
dependencies { implementation("androidx.tracing:tracing-ktx:2.0.5") implementation("androidx.core:core:1.19.1") }
Groovy
dependencies { implementation 'androidx.tracing:tracing:2.0.5' implementation 'androidx.core:core:1.19.1' }
System-Trace aufzeichnen
Nachdem Sie die erforderlichen Abhängigkeiten hinzugefügt haben, verwenden Sie den folgenden Code, um einen System-Trace aufzuzeichnen. In diesem Beispiel wird gezeigt, wie Sie eine Profilerstellungssitzung über eine zusammensetzbare Funktion starten und gleichzeitig rechenintensive Vorgänge sicher außerhalb des Hauptthreads verwalten.
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();
}
Der Beispielcode richtet die Profilerstellungssitzung ein und verwaltet sie. Dazu werden die folgenden Schritte ausgeführt:
Executor einrichten Erstellen Sie einen
Executor, um den Thread zu definieren, der die Profilerstellungsergebnisse empfängt. Die Profilerstellung erfolgt im Hintergrund. Wenn Sie einen Executor verwenden, der nicht im UI-Thread ausgeführt wird, können Sie ANR-Fehler (App antwortet nicht) vermeiden, falls Sie dem Callback später weitere Verarbeitungsschritte hinzufügen.Profiling-Ergebnisse verarbeiten Erstellen Sie ein
Consumer<ProfilingResult>-Objekt. Das System verwendet dieses Objekt, um Profilerstellungsergebnisse vonProfilingManageran Ihre App zurückzusenden.Erstellen Sie die Profilerstellungsanfrage. Erstellen Sie eine
SystemTraceRequestBuilder, um Ihre Profilerstellungssitzung einzurichten. Mit diesem Builder können Sie dieProfilingManager-Trace-Einstellungen anpassen. Das Anpassen des Builders ist optional. Wenn Sie dies nicht tun, verwendet das System die Standardeinstellungen.- Tag definieren Verwenden Sie
setTag(), um dem Trace-Namen ein Tag hinzuzufügen. Mit diesem Tag können Sie den Trace identifizieren. - Optional: Legen Sie die Dauer fest. Mit
setDurationMs()geben Sie die Profildauer in Millisekunden an. Mit60000wird beispielsweise ein 60-Sekunden-Trace festgelegt. Der Trace endet automatisch nach der angegebenen Dauer, wennCancellationSignalnicht vorher ausgelöst wird. - Wählen Sie eine Pufferrichtlinie aus. 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 eine kontinuierliche Aufzeichnung der letzten Aktivitäten beibehalten. - Legen Sie eine Puffergröße fest. Mit
setBufferSizeKb()können Sie eine Puffergröße für das Tracing angeben, mit der Sie die Größe der Ausgabe-Trace-Datei steuern können.
- Tag definieren Verwenden Sie
Optional: Sitzungslebenszyklus verwalten. Erstellen Sie einen
CancellationSignal. Mit diesem Objekt können Sie die Profilerstellungssitzung jederzeit beenden und so die Dauer genau steuern.Starten und Ergebnisse erhalten. Wenn Sie
requestProfiling()aufrufen, startetProfilingManagerim Hintergrund eine Profilerstellungssitzung. Nach Abschluss der Profilerstellung wird dieProfilingResultan die MethoderesultCallback#acceptgesendet. Wenn die Profilerstellung erfolgreich abgeschlossen wurde, gibtProfilingResultüberProfilingResult#getResultFilePathden Pfad an, unter dem der Trace auf Ihrem Gerät gespeichert wurde. Sie können diese Datei programmatisch oder für die lokale Profilerstellung durch Ausführen vonadb pull <trace_path>auf Ihrem Computer abrufen.Benutzerdefinierte Trace-Punkte hinzufügen Sie können im Code Ihrer Anwendung benutzerdefinierte Trace-Punkte hinzufügen. Im vorherigen Codebeispiel wird mit dem Block
trace("MyApp:HeavyOperation") { ... }ein benutzerdefiniertes Slice im generierten Profil erstellt.