本頁面說明如何使用 ProfilingManager API 記錄系統追蹤記錄。
ProfilingManager 也可以記錄其他設定檔類型。這項程序與記錄系統追蹤記錄類似,但每種追蹤記錄都使用不同的建構工具。支援的設定檔和建構工具如下:
系統追蹤記錄:使用
SystemTraceRequestBuilder記錄,有助於分析延遲時間和一般效能偵錯。記憶體快照資料:使用
JavaHeapDumpRequestBuilder記錄,有助於偵測記憶體流失問題及進行最佳化。堆積設定檔:使用
HeapProfileRequestBuilder記錄,有助於最佳化記憶體。呼叫堆疊設定檔:使用
StackSamplingRequestBuilder記錄,有助於瞭解執行程式碼情形和延遲分析。
新增依附元件
如要獲得最佳 ProfilingManager API 體驗,請將下列 Jetpack 程式庫新增至 build.gradle.kts 檔案。
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' }
錄製系統追蹤記錄
新增必要依附元件後,請使用下列程式碼記錄系統追蹤記錄。這個範例說明如何從可組合函式啟動剖析工作階段,同時安全地管理主執行緒以外的繁重作業。
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();
}
程式碼範例會執行下列步驟,設定及管理剖析工作階段:
設定執行器。建立
Executor,定義接收剖析結果的執行緒。剖析作業會在背景執行。如果您稍後要在回呼中新增更多處理作業,使用非 UI 執行緒執行器有助於避免發生應用程式無回應 (ANR) 錯誤。處理剖析結果。建立
Consumer<ProfilingResult>物件。 系統會使用這個物件,將ProfilingManager的剖析結果傳回應用程式。建立剖析要求。建立
SystemTraceRequestBuilder來設定剖析工作階段。這個建構工具可讓您自訂ProfilingManager追蹤設定。您可以選擇是否要自訂建構工具,如果沒有,系統會使用預設設定。- 定義標記。使用
setTag()為追蹤名稱新增標記。這個標記有助於識別追蹤記錄。 - 選用:設定時間長度。使用
setDurationMs()指定要分析的毫秒數。舉例來說,60000會設定 60 秒的追蹤記錄。如果未在指定時間內觸發CancellationSignal,追蹤作業會在時間到期後自動結束。 - 選擇緩衝區政策。使用
setBufferFillPolicy()定義追蹤資料的儲存方式。BufferFillPolicy.RING_BUFFER表示緩衝區已滿時,新資料會覆寫最舊的資料,持續記錄近期活動。 - 設定緩衝區空間。使用
setBufferSizeKb()指定追蹤記錄的緩衝區空間,藉此控制輸出追蹤記錄檔案的大小。
- 定義標記。使用
選用:管理工作階段生命週期。建立
CancellationSignal。 您可以隨時停止剖析工作階段,精確控制工作階段長度。開始檢測並接收結果。撥打
requestProfiling()時,ProfilingManager會在背景啟動剖析工作階段。完成剖析後,系統會將ProfilingResult傳送至您的resultCallback#accept方法。如果剖析作業順利完成,ProfilingResult會透過ProfilingResult#getResultFilePath提供追蹤記錄在裝置上的儲存路徑。您可以透過程式輔助方式取得這個檔案,也可以在電腦上執行adb pull <trace_path>進行本機剖析。新增自訂追蹤點。您可以在應用程式的程式碼中新增自訂追蹤點。在先前的程式碼範例中,
trace("MyApp:HeavyOperation") { ... }區塊會在產生的設定檔中建立自訂切片。