تحديد المواصفات الشخصية استنادًا إلى التطبيق

توضّح هذه الصفحة كيفية تسجيل تتبُّع النظام باستخدام واجهة برمجة التطبيقات ProfilingManager.

يمكن لحساب ProfilingManager أيضًا تسجيل أنواع أخرى من الملفات الشخصية. تشبه هذه العملية تسجيل تتبُّع النظام، ولكن يستخدم كل نوع أداة إنشاء مختلفة. في ما يلي الملفات الشخصية المتوافقة وأدوات الإنشاء الخاصة بها:

  • عمليات تتبُّع النظام: يتم تسجيلها باستخدام SystemTraceRequestBuilder، وهي مفيدة لتحليل وقت الاستجابة وتصحيح الأخطاء العامة في الأداء.

  • لقطات أجزاء من الذاكرة: يتم تسجيلها باستخدام JavaHeapDumpRequestBuilder، وهي مفيدة في رصد تسرُّب الذاكرة وتحسينها.

  • ملفات تعريف الذاكرة المخصّصة لأخذ لقطات لعناصر متعدّدة: يتم تسجيلها باستخدام HeapProfileRequestBuilder، وهي مفيدة لتحسين استخدام الذاكرة.

  • الملفات الشخصية لحزمة التنفيذ: يتم تسجيلها باستخدام StackSamplingRequestBuilder، وهي مفيدة لفهم تنفيذ الرمز البرمجي وتحليل وقت الاستجابة.

إضافة التبعيات

للحصول على أفضل تجربة مع واجهة برمجة التطبيقات ProfilingManager، أضِف مكتبات Jetpack التالية إلى ملف build.gradle.kts.

Kotlin

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

أنيق

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

يُعدّ نموذج الرمز البرمجي جلسة تحديد الأداء ويديرها من خلال اتّباع الخطوات التالية:

  1. إعداد المنفّذ أنشئ Executor لتحديد سلسلة التعليمات البرمجية التي ستتلقّى نتائج تحديد المشاكل. يتم إنشاء الملفات الشخصية في الخلفية. يساعد استخدام منفّذ سلسلة تعليمات غير تابعة لواجهة المستخدم في تجنُّب أخطاء "التطبيق لا يستجيب" (ANR) في حال أضفت المزيد من المعالجة إلى دالة الرجوع لاحقًا.

  2. التعامل مع نتائج التحليل أنشئ عنصر Consumer<ProfilingResult>. يستخدم النظام هذا العنصر لإرسال نتائج تحديد المشاكل من ProfilingManager إلى تطبيقك.

  3. أنشئ طلب إنشاء الملف الشخصي. أنشئ SystemTraceRequestBuilder لإعداد جلسة تحديد المشاكل. يتيح لك هذا المنشئ تخصيص ProfilingManager إعدادات التتبُّع. تخصيص أداة الإنشاء أمر اختياري، وفي حال عدم تخصيصها، سيستخدم النظام الإعدادات التلقائية.

    • تحديد علامة: استخدِم setTag() لإضافة علامة إلى اسم التتبُّع. تساعدك هذه العلامة في تحديد التتبُّع.
    • اختياري: حدِّد المدة. استخدِم setDurationMs() لتحديد مدة إنشاء الملف الشخصي بالمللي ثانية. على سبيل المثال، تضبط 60000 عملية تتبُّع لمدة 60 ثانية. ينتهي التتبُّع تلقائيًا بعد المدة المحدّدة إذا لم يتم تشغيل 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") { ... } شريحة مخصّصة في الملف الشخصي الذي تم إنشاؤه.