توضّح هذه الصفحة كيفية تسجيل عملية تتبُّع للنظام باستخدام واجهة برمجة التطبيقات ProfilingManager.
يمكن أن تسجِّل واجهة برمجة التطبيقات ProfilingManager أيضًا أنواعًا أخرى من الملفات الشخصية. تشبه هذه العملية تسجيل عملية تتبُّع للنظام، ولكن يستخدم كل نوع أداة إنشاء مختلفة. الملفات الشخصية المتوافقة وأدوات الإنشاء الخاصة بها هي:
عمليات تتبُّع النظام: يتم تسجيلها باستخدام
SystemTraceRequestBuilder، وهي مفيدة لتحليل وقت الاستجابة وتحديد المشاكل العامة في الأداء.لقطات أجزاء من الذاكرة: يتم تسجيلها باستخدام
JavaHeapDumpRequestBuilder، وهي مفيدة لرصد تسرّب الذاكرة وتحسينها.الملفات الشخصية للذاكرة: يتم تسجيلها باستخدام
HeapProfileRequestBuilder، وهي مفيدة لتحسين الذاكرة.الملفات الشخصية لحزمة التنفيذ: يتم تسجيلها باستخدام
StackSamplingRequestBuilder، وهي مفيدة لفهم تطبيق الرموز البرمجية وتحليل وقت الاستجابة.
إضافة التبعيات
للحصول على أفضل تجربة مع واجهة برمجة التطبيقات ProfilingManager، أضِف مكتبات Jetpack التالية إلى ملف build.gradle.kts.
Kotlin
dependencies { implementation("androidx.tracing:tracing-ktx:2.0.1") implementation("androidx.core:core:1.19.0") }
أنيق
dependencies { implementation 'androidx.tracing:tracing:2.0.1' 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لتحديد سلسلة التعليمات التي ستتلقّى نتائج التحليل. يتم التحليل في الخلفية. يساعد استخدام أداة تنفيذ لسلسلة تعليمات غير تابعة لواجهة المستخدم في منع أخطاء "التطبيق لا يستجيب" إذا أضفت المزيد من المعالجة إلى معاودة الاتصال لاحقًا.التعامل مع نتائج التحليل: أنشِئ عنصر
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") { ... }شريحة مخصّصة في الـ ملف الشخصي الذي تم إنشاؤه.