Na tej stronie dowiesz się, jak zarejestrować ślad systemowy za pomocą interfejsu ProfilingManager API.
ProfilingManager może też rejestrować inne typy profili. Ten proces jest podobny do rejestrowania śladu systemowego, ale każdy typ używa innego narzędzia do tworzenia. Obsługiwane profile i ich konstruktory to:
Ślady systemowe: rejestrowane za pomocą
SystemTraceRequestBuilder, które są przydatne do analizy opóźnień i ogólnego debugowania wydajności.Zrzuty sterty: rejestrowane za pomocą
JavaHeapDumpRequestBuilder, które są przydatne do wykrywania wycieków pamięci i optymalizacji.Profile sterty: rejestrowane za pomocą
HeapProfileRequestBuilder, które są przydatne do optymalizacji pamięci.Profile stosu wywołań: nagrywane za pomocą
StackSamplingRequestBuilder, które są przydatne do analizowania wykonywania kodu i opóźnień.
Dodawanie zależności
Aby w pełni korzystać z interfejsu ProfilingManager API, dodaj do pliku build.gradle.kts te biblioteki Jetpack:
Kotlin
dependencies { implementation("androidx.tracing:tracing-ktx:2.0.2") implementation("androidx.core:core:1.19.0") }
Dynamiczny
dependencies { implementation 'androidx.tracing:tracing:2.0.2' implementation 'androidx.core:core:1.19.0' }
Nagrywanie śledzenia systemu
Po dodaniu wymaganych zależności użyj tego kodu, aby zarejestrować ślad systemowy. Ten przykład pokazuje, jak rozpocząć sesję profilowania z poziomu funkcji kompozycyjnej, bezpiecznie zarządzając złożonymi operacjami poza wątkiem głównym.
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();
}
Przykładowy kod konfiguruje sesję profilowania i zarządza nią, wykonując te czynności:
Skonfiguruj wykonawcę. Utwórz
Executor, aby zdefiniować wątek, który będzie otrzymywać wyniki profilowania. Profilowanie odbywa się w tle. Użycie wykonawcy wątku innego niż wątek interfejsu pomaga uniknąć błędów typu „Aplikacja nie odpowiada” (ANR), jeśli później dodasz do wywołania zwrotnego więcej przetwarzania.Obsługa wyników profilowania. Utwórz obiekt
Consumer<ProfilingResult>. System używa tego obiektu do wysyłania wyników profilowania zProfilingManagerz powrotem do aplikacji.Utwórz żądanie profilowania. Utwórz
SystemTraceRequestBuilder, aby skonfigurować sesję profilowania. Ten kreator umożliwia dostosowywanie ustawień śledzeniaProfilingManager. Dostosowanie narzędzia do tworzenia jest opcjonalne. Jeśli tego nie zrobisz, system użyje ustawień domyślnych.- Zdefiniuj tag. Użyj ikony
setTag(), aby dodać tag do nazwy śladu. Ten tag pomoże Ci zidentyfikować ślad. - Opcjonalnie: ustaw czas trwania. Użyj
setDurationMs(), aby określić, jak długo ma trwać profilowanie (w milisekundach). Na przykład60000ustawia ślad o długości 60 sekund. Śledzenie kończy się automatycznie po określonym czasie trwania, jeśli przed jego upływem nie zostanie wywołane zdarzenieCancellationSignal. - Wybierz zasadę buforowania. Użyj parametru
setBufferFillPolicy(), aby określić sposób przechowywania danych śledzenia.BufferFillPolicy.RING_BUFFERoznacza, że gdy bufor jest pełny, nowe dane zastępują najstarsze dane, dzięki czemu zachowywany jest ciągły zapis ostatniej aktywności. - Ustaw rozmiar bufora. Użyj
setBufferSizeKb(), aby określić rozmiar bufora śledzenia, za pomocą którego możesz kontrolować rozmiar pliku wyjściowego śledzenia.
- Zdefiniuj tag. Użyj ikony
Opcjonalnie: zarządzaj cyklem życia sesji. Utwórz
CancellationSignal. Ten obiekt umożliwia zatrzymanie sesji profilowania w dowolnym momencie, co daje precyzyjną kontrolę nad jej długością.Rozpocznij i uzyskaj wyniki. Gdy zadzwonisz pod numer
requestProfiling(),ProfilingManagerrozpocznie sesję profilowania w tle. Po zakończeniu profilowania wysyłaProfilingResultdo TwojejresultCallback#accept. Jeśli profilowanie zakończy się pomyślnie,ProfilingResultpoda ścieżkę, w której ślad został zapisany na urządzeniu, za pomocąProfilingResult#getResultFilePath. Ten plik możesz uzyskać programowo lub w przypadku profilowania lokalnego, uruchamiającadb pull <trace_path>na komputerze.Dodaj niestandardowe punkty śledzenia. W kodzie aplikacji możesz dodawać niestandardowe punkty śledzenia. W poprzednim przykładzie kodu blok
trace("MyApp:HeavyOperation") { ... }tworzy niestandardowy wycinek w wygenerowanym profilu.