نمایه‌سازی با هدایت برنامه

این صفحه نحوه ضبط ردگیری سیستم را بااستفاده از ProfilingManager API نشان می‌دهد.

ProfilingManager می‌تواند انواع دیگر نمایه را نیز ضبط کند. این فرایند مشابه ضبط ردگیری سیستم است، اما هر نوع از سازنده متفاوتی استفاده می‌کند. نمایه‌های پشتیبانی‌شده و سازندگان آن‌ها عبارت‌اند از:

  • ردگیری‌های سیستم: بااستفاده از SystemTraceRequestBuilder ضبط شده است که برای تحلیل تأخیر و اشکال‌زدایی عملکرد کلی مفید است.

  • رونوشت‌های پشته: بااستفاده از JavaHeapDumpRequestBuilder ضبط شده‌اند که برای تشخیص نشت حافظه و بهینه‌سازی مفید هستند.

  • نمایه‌های پشته: بااستفاده از HeapProfileRequestBuilder ضبط شده‌اند که برای بهینه‌سازی حافظه مفید هستند.

  • نمایه‌های پشته فراخوانی: بااستفاده از StackSamplingRequestBuilder ضبط شده‌اند، که برای درک اجرای کد و تحلیل تأخیر مفید هستند.

افزودن وابستگی‌ها

برای داشتن بهترین تجربه با ProfilingManager API، کتابخانه‌های Jetpack زیر را به فایل build.gradle.kts خود اضافه کنید.

کاتلین

   dependencies {
       implementation("androidx.tracing:tracing-ktx:2.0.5")
       implementation("androidx.core:core:1.19.1")
   }
   

شیک

   dependencies {
       implementation 'androidx.tracing:tracing:2.0.5'
       implementation 'androidx.core:core:1.19.1'
   }
   

ضبط ردیابی سیستم

پس‌از افزودن وابستگی‌های لازم، از کد زیر برای ضبط ردگیری سیستم استفاده کنید. این مثال نشان می‌دهد که چگونه می‌توان یک جلسه نمایه‌سازی را از یک composable شروع کرد و درعین‌حال عملیات سنگین را به‌طور ایمن خارج از رشته اصلی مدیریت کرد.

کاتلین

@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.
}

جاوا

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

کد نمونه با طی کردن مراحل زیر، جلسه نمایه‌سازی را راه‌اندازی و مدیریت می‌کند:

  1. مجری را تنظیم کنید. Executor ایجاد کنید تا رشته‌ای را که نتایج نمایه‌سازی را دریافت خواهد کرد تعریف کنید. نمایه‌سازی در پس‌زمینه انجام می‌شود. استفاده از اجراکننده رشته غیرمیانای کاربر به جلوگیری از خطاهای «برنامه پاسخ نمی‌دهد» (ANR) کمک می‌کند اگر بعداً پردازش بیشتری به callback اضافه کنید.

  2. نتایج نمایه‌سازی را مدیریت کنید. یک شیء Consumer<ProfilingResult> ایجاد کنید. سیستم از این شیء برای ارسال نتایج نمایه‌سازی از ProfilingManager به برنامه شما استفاده می‌کند.

  3. درخواست نمایه‌سازی را بسازید. برای راه‌اندازی جلسه نمایه‌سازی، SystemTraceRequestBuilder ایجاد کنید. این سازنده به شما امکان می‌دهد تنظیمات ردیابی ProfilingManager را سفارشی‌سازی کنید. سفارشی‌سازی سازنده اختیاری است؛ اگر این کار را نکنید، سیستم از تنظیمات پیش‌فرض استفاده می‌کند.

    • برچسبی را تعریف کنید. از setTag() برای افزودن برچسب به نام ردگیری استفاده کنید. این برچسب به شما کمک می‌کند ردیابی را شناسایی کنید.
    • اختیاری: مدت زمان را تنظیم کنید. از setDurationMs() برای مشخص کردن مدت زمان نمایه‌سازی برحسب میلی‌ثانیه استفاده کنید. برای مثال، 60000 ردیابی ۶۰ ثانیه‌ای تنظیم می‌کند. اگر CancellationSignal قبل‌از آن راه‌اندازی نشود، ردیابی به‌طور خودکار پس‌از مدت مشخص‌شده به‌پایان می‌رسد.
    • یک خط‌مشی بافر انتخاب کنید. از setBufferFillPolicy() برای تعریف نحوه ذخیره داده‌های ردیابی استفاده کنید. BufferFillPolicy.RING_BUFFER یعنی وقتی میان‌گیر پر می‌شود، داده‌های جدید روی قدیمی‌ترین داده‌ها بازنویسی می‌شود و سابقه پیوسته‌ای از فعالیت‌های اخیر حفظ می‌شود.
    • اندازه میان‌گیر را تنظیم کنید. از setBufferSizeKb() برای مشخص کردن اندازه میان‌گیر برای ردیابی استفاده کنید که می‌توانید از آن برای کنترل اندازه فایل ردیابی خروجی استفاده کنید.
  4. اختیاری: چرخه حیات جلسه را مدیریت کنید. CancellationSignal ایجاد کنید. این شیء به شما امکان می‌دهد جلسه نمایه‌سازی را هر زمان که خواستید متوقف کنید و کنترل دقیقی بر طول آن داشته باشید.

  5. شروع کنید و نتایج را دریافت کنید. وقتی با requestProfiling() تماس می‌گیرید، ProfilingManager جلسه نمایه‌سازی را در پس‌زمینه شروع می‌کند. وقتی نمایه‌سازی انجام شد، ProfilingResult را به روش resultCallback#accept شما ارسال می‌کند. اگر نمایه‌سازی باموفقیت به‌پایان برسد، ProfilingResult مسیری را که ردگیری در آن در دستگاهتان ذخیره شده است ازطریق ProfilingResult#getResultFilePath ارائه می‌دهد. می‌توانید این فایل را به‌صورت برنامه‌نویسی‌شده یا برای نمایه‌سازی محلی با اجرای adb pull <trace_path> از رایانه‌تان دریافت کنید.

  6. افزودن نقاط ردیابی سفارشی. می‌توانید نقاط ردیابی سفارشی را در کد برنامه‌تان اضافه کنید. در نمونه کد قبلی، بلوک trace("MyApp:HeavyOperation") { ... } یک برش سفارشی در نمایه تولیدشده ایجاد می‌کند.