Pembuatan profil yang didorong aplikasi

Halaman ini menunjukkan cara merekam pelacakan sistem menggunakan ProfilingManager API.

ProfilingManager juga dapat merekam jenis profil lainnya. Proses ini mirip dengan merekam pelacakan sistem, tetapi setiap jenis menggunakan builder yang berbeda. Profil yang didukung dan buildernya adalah:

Menambahkan dependensi

Untuk mendapatkan pengalaman terbaik dengan ProfilingManager API, tambahkan library Jetpack berikut ke file build.gradle.kts Anda.

Kotlin

   dependencies {
       implementation("androidx.tracing:tracing-ktx:2.0.1")
       implementation("androidx.core:core:1.19.0")
   }
   

Groovy

   dependencies {
       implementation 'androidx.tracing:tracing:2.0.1'
       implementation 'androidx.core:core:1.19.0'
   }
   

Merekam pelacakan sistem

Setelah menambahkan dependensi yang diperlukan, gunakan kode berikut untuk merekam pelacakan sistem. Contoh ini menunjukkan cara memulai sesi pembuatan profil dari composable sambil mengelola operasi berat dengan aman di luar thread utama.

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();
}

Kode contoh menyiapkan dan mengelola sesi pembuatan profil dengan melalui langkah-langkah berikut:

  1. Menyiapkan eksekutor. Buat Executor untuk menentukan thread yang akan menerima hasil pembuatan profil. Pembuatan profil terjadi di latar belakang. Menggunakan eksekutor thread non-UI membantu mencegah error Aplikasi Tidak Merespons (ANR) jika Anda menambahkan lebih banyak pemrosesan ke callback nanti.

  2. Menangani hasil pembuatan profil. Buat objek Consumer<ProfilingResult>. Sistem menggunakan objek ini untuk mengirim hasil pembuatan profil dari ProfilingManager kembali ke aplikasi Anda.

  3. Membuat permintaan pembuatan profil. Buat SystemTraceRequestBuilder untuk menyiapkan sesi pembuatan profil. Builder ini memungkinkan Anda menyesuaikan setelan pelacakan ProfilingManager. Penyesuaian builder bersifat opsional; jika tidak, sistem akan menggunakan setelan default.

    • Menentukan tag. Gunakan setTag() untuk menambahkan tag ke nama pelacakan. Tag ini membantu Anda mengidentifikasi pelacakan.
    • Opsional: Menetapkan durasi. Gunakan setDurationMs() untuk menentukan durasi pembuatan profil dalam milidetik. Misalnya, 60000 menetapkan pelacakan 60 detik. Pelacakan akan otomatis berakhir setelah durasi yang ditentukan jika CancellationSignal tidak dipicu sebelumnya.
    • Memilih kebijakan buffer. Gunakan setBufferFillPolicy() untuk menentukan cara data pelacakan disimpan. BufferFillPolicy.RING_BUFFER berarti bahwa saat buffer penuh, data baru akan menimpa data terlama, sehingga menyimpan catatan aktivitas terbaru secara berkelanjutan.
    • Menetapkan ukuran buffer. Gunakan setBufferSizeKb() untuk menentukan ukuran buffer untuk pelacakan yang dapat Anda gunakan untuk mengontrol ukuran file pelacakan output.
  4. Opsional: Mengelola siklus proses sesi. Buat CancellationSignal. Objek ini memungkinkan Anda menghentikan sesi pembuatan profil kapan saja, sehingga Anda dapat mengontrol durasinya secara akurat.

  5. Memulai dan menerima hasil. Saat Anda memanggil requestProfiling(), ProfilingManager akan memulai sesi pembuatan profil di latar belakang. Setelah pembuatan profil selesai, `ProfilingManager` akan mengirim ProfilingResult ke metode resultCallback#accept Anda. Jika pembuatan profil berhasil diselesaikan, the ProfilingResult akan memberikan jalur tempat pelacakan disimpan di perangkat Anda melalui ProfilingResult#getResultFilePath. Anda dapat memperoleh file ini secara terprogram atau, untuk pembuatan profil lokal, dengan menjalankan adb pull <trace_path> dari komputer Anda.

  6. Menambahkan titik pelacakan kustom. Anda dapat menambahkan titik pelacakan kustom dalam kode aplikasi. Dalam contoh kode sebelumnya, blok trace("MyApp:HeavyOperation") { ... } membuat potongan kustom dalam profil yang dihasilkan.