الأدوات والأداء

تتبُّع الأخطاء: قياس الأداء تلقائيًا وتحديد الملفات الشخصية في الحقل

سماعات

  • راهول رافيكومار

    مسؤول مهندسي البرمجيات

لمحة

لمحة عن المحاضرة

يتطلّب تحسين تطبيقات Android معرفة عميقة بمسارات العمل المتزامنة وتعليق الكوروتين.

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

تقدّم هذه الجلسة مخططًا معماريًا لتقرير الملاحظات والآراء حول الأداء المستمر.

‫androidx.tracing 2.0: يمكنك تبديل الخلفيات ديناميكيًا ونشر سياقات الكوروتين (تدفقات Perfetto) عبر سلاسل المحادثات.

القياس الذكي: يمكنك استخدام تتبُّع ART المحلي للعثور على النقاط الفعّالة، ثم أتمتة إدخال نقاط تتبُّع منخفضة النفقات.

قياس الأداء الكلي: تتبَّع حالات التراجع وتحقَّق من تحسُّن الأداء على الأجهزة الفعلية في عملية التكامل المستمر.

تتبُّع الحقول: يمكنك تجاوز التتبُّع المحلي وتتبُّع CI باستخدام ProfilingManager.

ملخّص

  • يحلّ الإصدار 2.0 من واجهة Tracing API محلّ واجهات برمجة التطبيقات القديمة لتتبُّع الأداء في Android، وذلك لإزالة حدود طول التصنيفات والتشغيل بسرعة أكبر بسبع مرات. وتتيح لك أيضًا إنشاء مخازن مخصّصة للأنظمة الخلفية، مثل OpenTelemetry.
  • يحدّد انتشار السياق العلاقات السببية بين سلاسل المحادثات والروتينات المشتركة والعمليات. يمكنك تتبُّع التنفيذ غير المتزامن باستخدام traceCoroutine أو تمرير رموز الانتشار إلى الرمز المستند إلى عمليات الاستدعاء.
  • يضيف الإصدار 1.13 من Jetpack Compose ميزة "تتبُّع إعادة الإنشاء"، باستخدام بيانات وصفية مخصّصة لإظهار سبب حدوث عمليات إعادة الإنشاء. تسجّل عمليات التتبُّع قيم الحالة المعدَّلة وحِزم التنفيذ الدقيقة التي أدّت إلى ظهورها.
  • تستخدم أدوات إنشاء Android الإصدار 2.0 من Tracing لرصد المشاكل وتسريع عمليات الإنشاء. تحسينات موجَّهة للتتبُّع جعلت تقليص حجم الرموز البرمجية في R8 أسرع بنسبة %25 وخفّضت أوقات إرسال Metalava بنسبة %15

Gemini Notebook

المواضيع

  • التتبع
  • android-performance
  • perfetto
  • context-propagation
  • jetpack-compose
  • كوروتين
  • تحديد المواصفات الشخصية لصاحب البيانات

النص

تنزيل النص

مقدّمة حول تتبُّع Android وتطوّره

  • راهول رافيكومار

    مرحبًا بالجميع.

  • اسمي "راهول".

  • أنا مهندس في فريق تجربة مطوّري برامج Android.

  • أركّز على أداء التطبيقات والمكتبات، وهذا الحديث يدور حول التتبُّع.

  • قبل أن نتعمق في التتبع، أريد إنشاء بعض المفردات المشتركة حتى تتمكن من المتابعة.

  • لذا، في أي وقت أشير فيه إلى التتبُّع في هذا الحديث، فإنني أتحدث عن التتبُّع الصريح، أي قياس الرمز يدويًا حتى تعرف ما يفعله التطبيق.

  • لا أتحدث عن أدوات التحليل التلقائية التي سأتناولها.

  • لذا، فإن التتبع اليدوي هو فن ترك مسارات في قاعدة الرموز البرمجية، حتى تتمكن من التفكير في الأمر لاحقًا عندما تواجه مشكلة في الأداء أو أي مشكلة أخرى في هذا الشأن.

  • وعادةً ما تكون أفضل بكثير من السجلّات.

  • لا أعلم ما إذا كنت قد اطّلعت على مخرجات Logcat لتطبيق كبير.

  • وعادةً ما يكون عبارة عن فقرة نصية ضخمة لا يمكنك فهمها بسهولة.

  • من ناحية أخرى، يُعدّ التتبُّع أفضل بكثير.

  • يسهل تصوّره.

  • لديك مُدد.

  • لديك بعض البنية.

  • لديك بعض الأدوات، لذا يمكنك حتى تقسيمها ومعرفة ما حدث بالضبط ومتى.

  • لذا، إنّها أفضل بكثير.

  • ويمكنك توجيه الوكلاء إلى تتبُّع معيّن وطرح الأسئلة.

  • يمكنك استخدام أدوات مثل Trace Processor ومقاييس الأداء، وحتى تحويل عمليات التتبُّع إلى مقاييس.

  • هذه هي كل الإجراءات التي يمكنك تنفيذها باستخدام عمليات التتبُّع والتي يصعب تنفيذها باستخدام Logcat.

  • من ناحية أخرى، فإن التوصيف، وأنا أتحدث عن التوصيف التلقائي، عادة ما يكون استجابة لمشكلة تعرفها.

  • على سبيل المثال، قد تلاحظ أنّ تطبيقك يبدأ ببطء أو أنّ هناك مشكلة ما، وتريد تضييق نطاق المشكلة ومعرفة ما يحدث.

  • وفي هذه المرحلة، إذا لم تكن لديك عمليات تتبُّع يدوية لمساعدتك، يمكنك الاستعانة بإنشاء الملفات الشخصية تلقائيًا.

  • يمكنك استخدام أداة مثل تتبُّع سجلّ الإجراءات في وقت تشغيل Android ‏(ART) حيث يمكنك قياس كل إدخال وإخراج لكل إجراء.

  • أو قد تبحث عن عينات من المكدس باستخدام أدوات مثل Simpleperf.

  • لذلك، بدلاً من استخدام كل إدخال وخروج من الطريقة والحصول على عبء كبير، يمكنك إلقاء نظرة على عينات حزمة التنفيذ، ومعرفة ما هو موجود في المسار الساخن لتطبيقك.

  • نحن في عام 2026، وذاكرة الوصول العشوائي (RAM) تمثّل مشكلة في كل مكان، لذا قد تبحث عن الذاكرة وتسرُّب الذاكرة ولقطات العناصر المتعدّدة.

  • إذًا، هذه أداة تحليل أخرى يمكنك الاستفادة منها.

  • يمكنك الاطّلاع على مسارات الاحتفاظ بالبيانات في الذاكرة المؤقتة.

  • يمكنك الاطّلاع على الرمز الذي يخصّص الذاكرة، وهذه هي جميع أنواع التحليلات التي يمكنك إجراؤها باستخدام هذه الأداة.

  • صحيح؟

  • الآن، لدينا هذه المفردات.

  • لنتحدّث الآن عن سبب إنشاء مكتبة جديدة.

  • واجهات برمجة التطبيقات للتتبُّع، تم تقديم واجهات برمجة التطبيقات 1.0 في الأصل في Android Jelly Bean.

  • كان ذلك في عام 2012.

  • أي قبل أكثر من 14 عامًا.

  • ومنذ ذلك الحين، لم تتطوّر واجهات برمجة التطبيقات بالقدر الذي قد تتصوّره.

  • أضفنا بعض الإضافات.

  • تمت إضافة الأحداث والعدّادات غير المتزامنة في Android 10.

  • كان ذلك في عام 2019.

  • وهذا كل ما في الأمر.

  • ولم تتطوّر واجهة برمجة التطبيقات منذ ذلك الحين.

  • ومع ذلك، تطوّر المنظومة المتكاملة بشكل كبير.

  • لذا، تحسّن تتبُّع النظام الأساسي بشكل كبير، ولكن لم يتم تطبيق أي من ذلك من حيث إضافات واجهة برمجة التطبيقات لك.

  • وهذا ما نريد تغييره.

  • المشكلة الكبيرة الأخرى هي أنّ التتبّع له واجهة خلفية ثابتة في الإصدار 1.0، ما يعني أنّه في أي وقت تضيف فيه قسم تتبّع في الرمز البرمجي، ينتهي به الأمر دائمًا في مخزن تتبّع النظام المؤقت.

  • ‫Atrace هو نظام Android الفرعي للتتبُّع الذي يملك هذه البيانات.

  • لذا، إذا أردت أخذ بيانات التتبُّع وتحويلها إلى بيانات قياس عن بُعد، سيكون ذلك صعبًا للغاية، لأنّك أولاً عليك معرفة كيفية تخزين atrace لتلك البيانات.

  • وهو تنسيق موثَّق جيدًا، ولكن قد يصعب استخدامه.

  • لذا، هذا أمر نريد إصلاحه.

  • لذلك، نريد أن نقدّم لك الأدوات اللازمة لنقل بيانات التتبُّع إلى أي مكان تريده.

  • وكان لدينا أيضًا قيد في الإصدار 1.0 من التتبُّع، حيث كانت التصنيفات تقتصر على 128 حرفًا.

  • قد يكون هذا القيد معقولاً في عام 2012، ولكن في عام 2026، يبدو غريبًا بعض الشيء.

  • وما يزيد الأمر سوءًا هو أنّه لا يمكنك أيضًا إضافة أي بيانات وصفية إضافية إلى التتبّع، ما يمثّل مشكلة إضافية.

  • وهناك أيضًا النفقات العامة.

  • لذلك، في أي وقت تقوم فيه بتتبع تطبيقك، يجب أن تكون على دراية تامة بالنفقات العامة، لأن فعل مراقبة النظام لا ينبغي أن يغير ما يفعله النظام تحت الغطاء.

  • لذا، إذا كانت هناك حالة سباق تريد إعادة إنتاجها، يجب ألا يؤدي التتبع إلى اختفاء حالة السباق.

  • لذلك، عليك التأكّد من الحفاظ على جميع الدلالات التي تستخدمها في الحالات الأخرى.

  • كانت أداة Tracing 1.0 تستهلك قدرًا كبيرًا من الموارد، وأردنا تحسين ذلك.

  • حسنًا

  • لقد تحدثت كثيرًا عن المشاكل.

  • لنلقِ الآن نظرة على بعض المشاكل بشكل مرئي ونرى كيف تتفاقم من الناحية العملية.

  • لدينا هنا تتبُّع Perfetto.

  • إنّه لتطبيق "com.example.app".

  • لدينا سلسلة محادثات رئيسية، ولدينا شريحة واحدة باسم initializeComponent.

  • الآن، من الناحية المثالية، نريد بعض التفاصيل الإضافية.

  • لذا، لنضِف تفصيلاً.

  • نعلم الآن أنّ الرقم ‎initializeComponent يتصل بالرقم ‎performInitialize.

  • لا نعرف حتى الآن المكوّن الذي يتم إعداده.

  • الآن، الأداة الوحيدة في حزام الأدوات لدينا مع التتبع 1 هي الاسم، لذلك نضيفها إلى اسم الشريحة.

  • الآن، تذكر أن أسماء شرائح التتبع تقتصر على 128 حرفًا، لذلك يتم اقتطاعها في التتبع.

  • الآن، لزيادة الطين بلة، فإن النفقات العامة للاقتطاع هي أيضًا نفقات عامة تتكبدها الآن عند التتبع.

  • لذا، ستواجه هذه المشكلة بسرعة كبيرة.

  • لذلك، أردنا حقًا حلّ هذه المشكلة.

  • لقد رأيت تتبُّع Perfetto، ورأيت كيف يبدو بصريًا.

  • والآن، لنلقِ نظرة على كيفية كتابة الرمز لإصدار أقسام التتبُّع هذه.

  • لدينا أيضًا واجهة برمجة التطبيقات initializeComponent.

  • تتلقّى هذه الدالة سياقًا واسمًا، وتستدعي performInitialize.

  • كل شيء على ما يرام حتى الآن.

  • الآن، نريد إضافة تتبُّع باستخدام واجهة برمجة التطبيقات Tracing 1.0 API، لذا يمكنك إجراء ذلك من خلال استيراد androidx.tracing.trace واستخدام مجموعة تتبُّع.

  • أدخِل اسمًا له.

  • هنا، يبلغ performInitialize.

  • ولكن كما أردنا أن نفعل من قبل، أردنا معرفة اسم المكوّن، لذلك سنستخدم استكمال السلسلة في Kotlin، وسنضيف الاسم.

  • وهنا تكمن المشكلة الأولى.

  • لدينا هنا الحدّ الأقصى لعدد الأحرف المسموح به وهو 128 حرفًا.

نظرة عامة على الإصدار 2.0 من ميزة "التتبُّع" والأداء

  • راهول رافيكومار

    يمكننا حلّ هذه المشكلة باستخدام Tracing 2.

  • بدلاً من استخدام androidx.tracing.trace، يمكنك استخدام Tracer.

  • تبدو واجهة برمجة التطبيقات متشابهة بشكل ملحوظ.

  • لديك Tracer.global.trace بدلاً من trace.

  • لديك فئة.

  • سنتحدّث عن الفئات بمزيد من التفصيل لاحقًا، ولكن بشكل أساسي، لديك اسم.

  • وبعد ذلك، لا يزال لديك مجموعة الرموز التي تحاول تتبعها.

  • والآن، لم يعُد هناك حدّ أقصى لعدد الأحرف يبلغ 128 حرفًا.

  • تحدّثتُ عن تتبُّع النفقات العامة.

  • أريد أن أتناول هذا الموضوع بمزيد من التفصيل وأرى كيف حسّنّا هذه الميزة.

  • على هاتف Pixel 6a يعمل بنظام التشغيل Android 16، استغرق إدراج قسم واحد للبداية والنهاية حوالي 1,200 نانو ثانية.

  • قد لا يبدو ذلك كثيرًا، ولكن إذا كان لديك رمز برمجي نشط يضيف الكثير من أقسام التتبُّع، فهذا يعني أنّ تطبيقك قد يقضي وقتًا أطول في عرض اللقطات.

  • لذلك، لا نريد أن نأخذ هذا الوقت منك.

  • لقد حسّنّا الأمور كثيرًا باستخدام حزمة تطوير البرامج (SDK) من AndroidX Perfetto.

  • كانت هذه حزمة SDK تجريبية قدّمناها لتتبُّع الإنشاء، وقد أدّى ذلك إلى تحسين الوضع كثيرًا.

  • لم نكن نريد منك شحن هذا لأنّ هذه هي حزمة تطوير البرامج (SDK) لـ Perfetto C مع روابط JNI، وحزمة تطوير البرامج (SDK) لـ C تبلغ حوالي 4 ميغابايت داخل حزمة APK.

  • لم نكن نتمنى أن تواجهك أي مشكلة.

  • لذا، مع Tracing 2، لدينا الآن مكتبة مخصّصة لهذا الغرض.

  • لقد حسّنا النفقات العامة بشكل كبير، ويسرّنا إعلامك بأنّ Tracing 2 أسرع بـ 7 أضعاف، ما يمنحك المزيد من الوقت للقيام بالمهام التي تريدها، ويستغرق وقتًا أقل في البنية الأساسية.

  • ما الذي يتيحه هذا الابتكار ولم يكن ممكنًا في السابق؟

  • ومن الأمثلة على ذلك ميزة تتبُّع وقت التشغيل في Metro.

  • ‫Metro هي مكتبة لحقن التبعية، لمن لا يعرفها.

  • تتيح استخدام Kotlin Multiplatform.

  • ويستخدم مكوّنًا إضافيًا لبرنامج تجميع Kotlin.

  • فهي تنشئ كل الرموز البرمجية لحقن التبعية، وتتكامل مع Tracing 2.0 لتسهيل عرض تكلفة كل @Inject في الرمز البرمجي.

  • إليك مثالاً على ذلك.

  • لنفترض أنّ لدينا رسمًا بيانيًا للتطبيق.

  • في Metro، يتم تحديد نطاق كل شيء في رسم بياني، وبالتالي لدينا حقل واحد، وهو AnalyticsTracker.

  • لدينا أيضًا ربط يوفّر الموارد.

  • لدينا عميل شبكة مع OkHttpClient كعملية تنفيذ، ثم يأتي تنفيذ AnalyticsTracker من AnalyticsTrackerImpl، ونحتاج إلى مثيل لعميل الشبكة، والذي يعرف الرسم البياني كيفية توفيره.

  • لذا، ينشئ المكوّن الإضافي لبرنامج التجميع رمزًا برمجيًا يبدو على النحو التالي.

  • لذلك، إذا كان لديك AnalyticsTracker، ينشئ دالة getter مثيلاً لعميل الشبكة ويمرره إلى دالة constructor لـ AnalyticsTrackerImpl، وهذا أمر جيد.

  • ولكن الآن مع تمكين التتبع، يمكنك الآن معرفة تكلفة إنشاء ليس فقط تنفيذ أداة التتبع، ولكن كل تبعية واحدة يحتاجها.

  • لذلك، نتيح لك الآن الاطّلاع على تكلفة إنشاء النوع، بالإضافة إلى جميع تبعياته في عملية تتبُّع، وهذا أمر رائع حقًا.

  • لذا، باستخدام هذه المعلومات، تمكّن تطبيق Wise المالي من تحسين وقت بدء تشغيله بنسبة %45.

  • وتمكّنوا من تأجيل العناصر التي لم تكن ضرورية للتهيئة أثناء بدء تشغيل التطبيق، كما حدّدوا بعض العناصر التي تم تحديد نطاقها بشكل خاطئ، حيث لم تكن العناصر التي كان من المفترض أن تكون أحادية في الواقع أحادية عن طريق الخطأ، وهذه أشياء يمكنك رؤيتها وإصلاحها بسهولة في الرمز البرمجي.

  • إذا نظرت إلى مقاييس بدء التشغيل، فستجدها رائعة حقًا.

  • لم يقتصر الأمر على تحسين وقت عرض الإطار الأوّلي، بل تم أيضًا تحسين وقت العرض الكامل.

  • يستخدم شريك آخر، وهو BandLab، المعلومات نفسها في CI.

  • وبالتالي، لا تقتصر معرفتك على تكلفة كل عملية ربط @Inject، بل تشمل أيضًا معرفة سلسلة التعليمات التي تسبّبت في هذه التكلفة.

  • بعد معرفة ذلك، يمكنك العثور على جميع عمليات الربط المكلفة في @Inject على سلسلة التعليمات الرئيسية وفي CI، يمكنك تشغيل طلب بحث لطلب قائمة بجميع عمليات الربط المكلفة ثم إرجاعها إلى التغيير الذي أدخلها، ويمكنك الآن إلقاء اللوم على التغيير الفعلي ومحاولة إصلاحه.

نشر السياق، وأنماط "كوروتين"، وJetpack Compose

  • راهول رافيكومار

    حسنًا

  • لقد تحدثت حتى الآن عن كل ما تم تحسينه مقارنةً بالإصدار الأول من Tracing، ولكن سأتحدث الآن عن الميزات الجديدة في الإصدار الثاني من Tracing والتي لم تكن متاحة من قبل.

  • لذا، تُسمّى إحدى الميزات الأساسية "نشر السياق" (Context Propagation)، وبما أنّ التطبيقات الحديثة تزداد تعقيدًا، يتم تشغيل أعباء العمل على مستوى سلاسل عمليات متعددة وروتينات مشتركة متعددة وعمليات متعددة.

  • تريد أن تكون قادرًا على تصور كيفية انتشار الطلب بين كل سلسلة مقابل عملية.

  • وهذا ما يتيحه "نشر السياق".

  • لذا، إذا أعطيتك قائمة بالشرائح بدون إخبارك بأي شيء عن سبب حدوث ذلك، فستكون مفيدة، ولكن ليس من السهل التفكير فيها.

  • ولكن بدلاً من ذلك، إذا أخبرتك أن هذه هي سلسلة الأحداث السببية التي حدثت، فسيصبح من الأسهل عليك معرفة ما كان يحدث ثم ربط تكلفة كل شريحة على حدة.

  • وهذا ما يوفّره "نشر السياق".

  • يسرّنا إعلامك بأنّ ميزة "نشر السياق" متاحة بالكامل، وإليك مثال حقيقي.

  • بالنظر إلى تتبُّع Perfetto لـ com.example.app، لنفترض أنّ performInitialize كان يستغرق وقتًا طويلاً وأردت تأجيل التكلفة أو نقلها إلى سلسلة محادثات مختلفة.

  • يمكنك تنفيذ ذلك بسهولة.

  • لنفترض أنّني نقلتُ "performInitialize" إلى سلسلة محادثات مختلفة.

  • الآن، فقدت الطبيعة الهرمية للشريحة.

  • لذلك، لم يعُد بإمكانك معرفة أنّ initializeComponent هو الذي تسبّب في حدوث performInitialize ما لم تطّلِع على الرمز.

  • في عالم مثالي، قد ترغب في أن يخبرك السهم أن (أ) تسبب في (ب) ومن ثم يمكنك نوعًا ما التفكير في سلسلة الأحداث السببية.

  • ربما رأيت سجلّ تتبّع النظام الذي نستخدمه في معاملات Binder على المنصة.

  • يمكنك هنا ملاحظة أنّ مقياس أداء Compose يُجري معاملة Binder مع system_server لإعداد إطار.

  • هذه الميزات كانت متاحة دائمًا من منظور تتبُّع المنصة، ولكن لم يسبق لنا توفير الأدوات لمطوّري التطبيقات.

  • لذا، أصبح "نشر سياق الكوروتين في Kotlin" متوافقًا بالكامل مع الإصدار 2.0 من Tracing.

  • ليس عليك اتّخاذ أي إجراء.

  • ما عليك سوى إخراجها من العلبة.

  • لدينا هنا طريقة تعليق واحدة تُسمى fetchProfile.

  • يستدعي client.get، وهي طريقة تعليق أخرى، ثم ينتقل إلى Dispatchers.Default لإلغاء تسلسل الاستجابة.

  • هذا رمز روتيني تقليدي جدًا، ومن المحتمل أنك تتعامل معه كل يوم.

  • لنفترض الآن أنّنا نريد تتبُّع هذا الإجراء.

  • نضيف قسمًا للتتبُّع.

  • لذا، بدلاً من trace، سنستخدم واجهة برمجة التطبيقات traceCoroutine لأنّها كتلة رمز برمجي معلَّقة، ثم سنضيف قسم تتبُّع داخلي آخر باستخدام واجهة برمجة التطبيقات traceCoroutine نفسها للاستدعاء الداخلي قبل تبديل السياق إلى Dispatchers.Default.

  • الآن، في التتبع، سترى شيئًا كهذا.

  • سترى أن عملنا بدأ في worker-1 ثم تم تبديل السياق فعليًا وانتقل إلى worker-3 وسترى أسهمًا مفيدة في التتبع تشير إلى ذلك.

  • ليس هذا فحسب، فإذا كبّرنا المربع في المنتصف، يمكننا أن نرى أنّ عملية إلغاء التسلسل بدأت في worker-1 وأنّ استدعاء withContext هو ما تسبّب في تبديل السياق، ثم استمرت العملية في worker-3، ثم تم استئناف fetchProfile في worker-3 لأنّ آلية الكوروتين تريد تقليل عدد عمليات تبديل السياق التي تحدث.

  • يمكنك معرفة وقت تعليق الكوروتين بالضبط، ووقت استئنافه، ويمكنك الاطّلاع على سلسلة الأحداث السببية الكاملة.

  • من الميزات الأخرى التي يوفّرها Tracing 2 هي إمكانية إضافة بيانات وصفية مخصّصة إلى عملية تتبُّع.

  • في السابق، كانت الأداة الوحيدة المتاحة هي اسم قسم التتبُّع، ولكن الآن، يمكنك إضافة مجموعة بيانات وصفية تتيح لك إضافة أي عدد تريده من إدخالات البيانات الوصفية.

  • ويمكنك إضافة ذلك إلى التتبع.

  • عند الاطّلاع على عملية التتبّع الفعلية، يمكنك أن ترى هنا أنّ جميع البيانات الوصفية التي أضفتها موجودة في عملية التتبّع لتستخدمها جميع الأدوات.

  • يمكنك الآن تضمين كل ما تعرفه، وكل المعلومات الإضافية التي أردت دائمًا تضمينها.

  • لقد قدّمتُ لكم مجموعة من الميزات المجرّدة التي تتيحها ميزة التتبُّع، ولكنّ الميزة الجديدة في Jetpack Compose التي تُبرز كل هذه الميزات هي "تتبُّع إعادة التركيب".

  • يتم شحن هذه الميزة كجزء من الإصدار 1.13 من Compose، لذا فهي جاهزة للاستخدام اليوم.

  • إليك مثالاً على ذلك.

  • هذه هي ميزة "تتبُّع التركيب" التي من المحتمل أن تكونوا على دراية بها جميعًا.

  • وهذا موجود منذ الأيام الأولى من Compose.

  • ويُطلعك على التكلفة النسبية للدالة المركّبة وجميع الشرائح الفرعية، وتكلفة كل منها في سلسلة المحادثات التي حدثت فيها.

  • هذا واضح ومباشر.

  • ما لا يخبرك به هو السياق.

  • ولا يخبرك عن سبب حدوث ذلك.

  • ولا يوضّح لك سبب إعادة إنشاء العناصر.

  • لذا، إذا قمت بالتصغير، فهذه هي المنطقة محل الاهتمام التي كنا ننظر إليها للتو في الشريحة السابقة.

  • وعلى يمين الشاشة، يمكنك رؤية حدث إدخال إرسال تم تنفيذه.

  • ربما هذا ما غيّر بعض الحالات، وربما هذا ما تسبب في إعادة التركيب.

  • ولكن الأمر غير واضح.

  • أنت لا تعرف على وجه اليقين.

  • ومع ذلك، من خلال تتبُّع إعادة الإنشاء، نضيف الآن بيانات وصفية مخصّصة إلى عملية التتبُّع توضّح لك سبب حدوث عمليات إعادة الإنشاء بالضبط.

  • ليس هذا فحسب، بل من السهل جدًا الآن معرفة أنّ هذه الأحداث مرتبطة ببعضها.

  • وإذا نقرت عليها، ستخبرك بالضبط بالحالة التي تغيرت.

  • سيخبرك أنّه كان حالة، وكان قيمة حالة قابلة للتغيير تم تبديلها إلى "خطأ"، والتي كانت في السابق "صحيح".

  • ليس هذا فحسب، بل سنخبرك أيضًا بحزمة التنفيذ التي تسبّبت في حدوثه.

  • هنا، يمكنكم ملاحظة أنّ AbstractClickableNode.performClick حدث، ما أدى إلى تغيير الحالة القابلة للتغيير، وبالتالي إلى إعادة الإنشاء.

  • هذه ميزة رائعة جدًا لم تكن متوفّرة من قبل.

  • تتوفّر هذه الأدوات أيضًا، عذرًا، تتوفّر كل هذه الوظائف أيضًا في أداة Android Performance Analyzer الجديدة، إذا أردت تجربة بعض الأدوات المستندة إلى الوكلاء.

  • وإلى اليمين

  • لقد ذكرتُ سابقًا أنّ ميزة "نشر السياق" متوافقة بالكامل مع الكوروتينات في Kotlin.

  • ولكن ماذا عن الرموز البرمجية التي تستخدم مزيجًا من الروتينات المساعدة والرموز البرمجية غير الروتينية، أو ربما تقوم بتشغيل سلاسل المحادثات بنفسك، أو ربما لديك بعض الرموز البرمجية القديمة جدًا التي تستخدم عمليات الاستدعاء لسبب ما؟

  • حسنًا

  • والخبر السار هو أنّه لا يزال بإمكانك الاستفادة من جميع مزايا نشر السياق.

  • لنفترض أنّ لدينا واجهة برمجة تطبيقات قديمة تستخدم دالة رد اتصال ومنفّذًا.

  • يستخدم التنفيذ executor.execute، ويستدعي runCatching، وهو تفصيل التنفيذ، ويحصل على الاستجابة، ثم يخبر دالة الاستدعاء عن الاستجابة.

  • حسنًا

  • الآن، إذا أردت تتبُّع ذلك، سأستخدم Tracer.global.trace، وأضيف تصنيفًا، ثم أحيط به الكتلة.

  • ولكن بما أنّني أريد أيضًا الحفاظ على سلسلة الأحداث السببية، سأقبل نوعًا جديدًا اختياريًا يُسمّى رمز الانتشار المميّز.

  • وما سأفعله هو أنني سأمرر الرمز المميز إلى واجهة برمجة تطبيقات التتبع الداخلية.

  • لذا، يمكن لأي شخص يستدعي هذه الطريقة أن يعطيني رمز الانتشار اختياريًا، حتى أتمكن من الحفاظ على سلسلة الأحداث السببية التي تحدث.

  • حسنًا

  • الآن، يمكن أن يكون المتصل بواجهة برمجة التطبيقات هذه واجهة برمجة تطبيقات معلّقة.

  • لدينا هنا دالة تعليق profileRequest، والتي ستستدعي الآن واجهة برمجة التطبيقات التي تستخدم دالة رد اتصال ومنفّذًا.

  • هنا، لأنني أربط بين عالمين، حيث أربط بين عالم التعليق وعالم الاستدعاء، سأستخدم أداة إنشاء suspendCoroutine.

  • سأحصل على استمرارية، ثم سأستدعي واجهة برمجة التطبيقات، ثم أستأنف الاستمرارية في رد الاتصال، بمجرد حصولي على الاستجابة.

  • وهنا المنفّذ الذي أحدّده باسم profileRequestContext.

  • وهو منفّذ أحادي السلسلة.

  • وبما أنّ traceCoroutine يضمّن رمزًا مميزًا للنشر داخل سياق الكوروتين، يمكنني الحصول على رمز النشر من داخل سياق الكوروتين، ثم تمريره كإدخال إلى واجهة برمجة التطبيقات القديمة، وبذلك يمكنني الحفاظ على سلسلة الأحداث السببية.

  • وفي التتبّع، يظهر ذلك بوضوح شديد.

  • لذا، لدينا هنا شيء بدأ في worker-3 وتم تبديله إلى المنفّذ أحادي السلسلة، بعد أن أجرينا تبديل السياق، ثم استؤنف مرة أخرى على worker.

  • لذا، إنّها ميزة رائعة.

التتبُّع العام والبدء المخصّص

  • راهول رافيكومار

    حسنًا

  • من الإجراءات الأخرى التي يمكنك اتّخاذها تخصيص Tracer.global.

  • حتى الآن، تمنحك واجهة Tracing 2.0 API إعدادات تلقائية جيدة جدًا لما يجب أن يكون عليه Tracer.global، ولكن إذا أردت تخصيصها، يمكنك ذلك.

  • بالإضافة إلى ذلك، يتوافق Tracing 2.0 مع JVM وAndroid لأنّنا نريد أن يكون التتبُّع متوافقًا بالكامل مع مكتبات Java وAndroid.

  • إذًا، الخطوات الثلاث للبدء هي إنشاء مصدر تتبُّع.

  • يُعدّ مخزن بيانات التتبُّع طريقة فعّالة لتخزين حزم بيانات التتبُّع.

  • ويحدّد تنسيق البيانات المنقولة وموقع آثار الإخراج حيث يتم تخزينها.

  • نوفّر لك عملية تنفيذ جاهزة لمستودع تتبُّع يتوافق مع تنسيق Perfetto، ولكن يمكنك بالطبع كتابة مستودع التتبُّع الخاص بك.

  • يعود ذلك إلى محاولة حلّ مشكلة أنّ atrace كانت الخلفية الوحيدة المتاحة في Tracing 1.

  • وأردنا حلّ هذه المشكلة.

  • لذا، إذا أردت الآن كتابة مصدر OpenTelemetry أو استخدام مصدرك الخاص، فهذا ممكن تمامًا.

  • ويمكنك اختيار إلغاء ذلك.

  • بعد إنشاء مصدر، يمكنك إنشاء برنامج تشغيل تتبُّع وإضافة المصدر إليه.

  • ويمكنك أيضًا اختيار الفئات التي تريد تفعيلها.

  • هذا هو الجزء الذي تتألق فيه الفئات حقًا.

  • لذلك، يمكنك أن تقرّر أنّ بعض الفئات في الإصدار أهم من غيرها، مقارنةً بوضع تصحيح الأخطاء، ويمكنك تحديد الفئات التي تريد تفعيلها.

  • أو يمكنك أيضًا أن تقول: "أريد تفعيل جميع الفئات بغض النظر عن مصدرها".

  • ولا بأس بذلك على الإطلاق.

  • يمكنك أيضًا إضافة سمات مخصّصة عامة إلى عملية التتبُّع.

  • لذا يمكنك تضمين عناصر مثل إصدار الإصدار أو النكهة أو أي بيانات وصفية أخرى تريد إرفاقها بالتتبُّع باستخدام واجهة برمجة التطبيقات addAttribute.

  • أخيرًا، يمكنك تسجيل أداة تتبُّع من خلال طلب setGlobalTracer.

  • هذا ما يربط Tracer.global بهذا المثيل من أداة التتبّع، وبالتالي، هذا ما يتوفّر للعملية.

  • على Android، نظرًا لأن العمليات لها دورة حياة محددة بشكل أفضل، كل ما عليك فعله هو تنفيذ AbstractTraceDriver.Factory، وتجاوز طريقة create، والقيام بالضبط بما فعلته من قبل.

  • لذلك، عليك إنشاء جهاز الاستقبال، ثم إنشاء برنامج التشغيل.

  • لا داعي للتسجيل هنا.

  • سنقوم بالتسجيل نيابةً عنك، لأننا نعلم أن هذا ما حدث أولاً.

  • حسنًا

  • حسنًا، أودّ أن أؤكّد مجددًا أنّ ميزة التتبُّع جاهزة للاستخدام على منصات متعددة.

  • هذا خيار مقصود للغاية لأننا نعلم أن Compose يعمل على منصات متعددة، ونريد أن نتيح لك إمكانية التتبع في أكبر عدد ممكن من الأماكن.

  • في الوقت الحالي، نتيح استخدام Android وJVM، ولكنّ طريقة التنفيذ تتيح لنا إمكانية استخدام أنظمة أساسية مستقبلية إذا أردنا ذلك.

تحسينات أداة الإنشاء (R8 وmetalava)

  • راهول رافيكومار

    يسرّنا إعلامك بأنّ أداة R8، وهي الأداة المستخدَمة لتقليص حجم الرمز المصدر وتحسينه على Android، تستخدم Tracing 2.0.

  • ومن خلال الاستفادة من عمليات التتبُّع، تمكّنا من تحسين R8 لتقليص الرموز البرمجية وتحسينها بشكل أسرع بنسبة %25.

  • وهذا يعني أوقات إنشاء أقصر، وأوقات انتظار أقصر لعمليات التحسين.

  • لن أستعرض كل تحسين أجريناه في R8، ولكن أريد فقط أن أستعرض تحسينًا واحدًا، لأعطيكم فكرة عما يتيحه التتبُّع.

  • لذا، فإنّ R8 هو في الأساس محوّل من مصدر إلى مصدر.

  • لذلك، تحصل على مجموعة من ملفات JAR، وتقوم بتقليصها وتحسينها، وتنتج مجموعة أخرى من ملفات JAR التي يمكنك تحويلها إلى dex.

  • لذا، يبدأ AGP بقراءة ملفات JAR، والتي لا تشمل تطبيقك فحسب، بل جميع تبعياته.

  • لذلك، يستخدم Gradle workers لبدء قراءة جميع ملفات JAR المُدخلة.

  • لديك العديد من ملفات JAR التي تريد قراءتها.

  • بعد الانتهاء من قراءة ملفات JAR، يبدأ R8 في تحليل الرمز البرمجي وإنشاء نموذج لما يمثّله الرمز وجميع نقاط الدخول الخاصة به، حتى يتمكّن من بدء تحسين الرمز وتقليل حجمه.

  • هذا هو الجزء الذي يمثّل الوقت الذي يكون فيه R8 جاهزًا لبدء عملية التحسين، أليس كذلك؟

  • قد تلاحظون بعض المساحة الفارغة في المنتصف، وقد تتساءلون عن سبب وجود وقت غير مستخدَم لوحدة المعالجة المركزية.

  • والسبب هو أنّ واجهة برمجة التطبيقات القديمة كانت تنتظر حتى يقرأ جميع العمال جميع ملفاتهم أولاً قبل تحليل أي شيء.

  • من الواضح أنّه يمكننا حلّ هذه المشكلة، وهذا ما فعلناه بالضبط.

  • لذلك، في الإصدار 9 من AGP، يمكنك ملاحظة أنّه بمجرد الانتهاء من قراءة ملفات JAR المُدخلة، بدأنا في معالجتها على الفور.

  • ولم نضطر إلى انتظار كل عامل حتى ينتهي من قراءة مدخلاته.

  • وقد أدى ذلك إلى تقليل الوقت الذي ننتظره لقراءة جميع المدخلات بشكل كبير.

  • هذا تحسين واضح يمكنك رؤيته بوضوح شديد عند النظر إلى التتبع، ولكنه ليس واضحًا عندما تنظر فقط إلى الرمز المصدر لمعرفة ما يستحق التحسين.

  • من الأدوات الأخرى التي نستخدمها يوميًا كمهندسي AndroidX هي Metalava.

  • ‫Metalava هي أداة نستخدمها للتأكّد من أنّ أي واجهات برمجة تطبيقات نطلقها تتوافق مع واجهات برمجة التطبيقات التي تتوقّعها، ويمكن للأشخاص الخارجيين استخدامها بالكامل بدون أي أخطاء نريد تجنّبها.

  • هذه أداة تعمل على كل واجهة برمجة تطبيقات عامة وواجهة برمجة تطبيقات مقيدة، وبالتالي فإن أي وقت نوفره هنا هو في الواقع وقت نستعيده كمهندس AndroidX.

  • استخدمت Metalava أيضًا ميزة التتبُّع لتحسين أوقات ما بعد الإرسال بنسبة هائلة بلغت %15.

  • ولشرح ما حدث، سنلقي نظرة على التتبُّع. تُجري أداة Metalava نوعَين من عمليات التحقّق: الأول هو التحقّق من واجهات برمجة التطبيقات المقيَّدة، والثاني هو التحقّق من واجهات برمجة التطبيقات العامة.

  • ولكن قد تلاحظ نمطًا.

  • نحن نقوم بمجموعة كاملة من الإعدادات لتشغيل هذه الفحوصات مرتين.

  • ما يحدث هو أنّه تتم قراءة جميع مدخلات البرنامج، وننشئ واجهة PSI، وهي واجهة بنية البرنامج، أو، كما تعلمون، شكل الرمز البرمجي الذي يمكن التفكير فيه.

  • بعد ذلك، ننشئ شجرة AST قبل البدء في تطبيق جميع عمليات التحقّق من الأخطاء التي نريدها.

  • ونحن نقوم بهذا العمل المكلف مرتين.

  • كيف حللنا هذه المشكلة؟

  • لقد فعلنا ذلك مرة واحدة فقط.

  • لذا، بدلاً من إجراء كل عمليات الإعداد هذه مرتين، أجريناها مرة واحدة فقط، وأعدنا استخدام هذه المدخلات لكل من عمليات التحقق المقيدة وكذلك عمليات التحقق العامة، وبالتالي حصلنا على كل هذه الوفورات.

  • أجل

  • حسنًا

أفضل الممارسات المتعلّقة بالتتبُّع وقياس الأداء

  • راهول رافيكومار

    بعد أن قدّمت لكم لمحة عن الإمكانيات التي تتيحها ميزة "التتبُّع 2.0"، من المهم معرفة أنّه على الرغم من أهمية التتبُّع، من المهم أيضًا معرفة ما يجب تتبُّعه.

  • لا تريد ترك آثار في كل مكان.

  • يجب أن تكون حذرًا جدًا بشأن ما تختاره، وسأحاول أن أقدّم لك بعض الإرشادات حول ما يستحق التتبّع.

  • لذا، فإن أول شيء يجب عليك فعله هو العثور على فجوات غير مفسرة في التتبع.

  • لذا، إذا كنت تنفّذ أي إجراء عندما تكون سلاسل التعليمات في حالة تشغيل أو قابلة للتشغيل، وظهرت فجوات واضحة في سجلّ تتبّع النظام، ولم تتمكّن من تفسيرها، ولم تكن تعرف ما يفعله تطبيقك، فقد يكون من المفيد التحقيق في الأمر.

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

  • عليك أيضًا تحديد الأولويات من خلال تجارب المستخدمين.

  • لذا، عليك الاطّلاع على بدء تشغيل التطبيق.

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

  • لذلك، في أي وقت لا يمكنك فيه التفكير في شيء ما، فهذا يستحق التتبع.

  • يجب أيضًا الاطّلاع على حزم التنفيذ العميقة أو استخدام وحدة المعالجة المركزية غير المبرَّر من خلال عملية تتبُّع ومحاولة معرفة ما يحدث بالضبط، ثم تحويل هذه الأجزاء من قاعدة الرموز البرمجية إلى عمليات تتبُّع.

  • لذلك، يمكنك تتبعها.

  • وبعد ذلك، في أي وقت تكون فيه سلاسل التعليمات في وضع السكون، عليك التأكّد من فهم سبب سكونها.

  • يجب أن تكون في انتظار الإدخال/الإخراج، أو في انتظار معاملة Binder، أو في انتظار بعض أحداث الإدخال الخارجية التي تتوقع حدوثها.

  • ولكن إذا كان لديك، كما تعلم، نوم غير مبرر، فهذه تستحق التحقيق للغاية لأنه من الواضح أنه قد يكون هناك شيء يكمن هناك.

  • وبعد ذلك، بمجرد أن تعرف، رش جميع أقسام التتبع في هذا الرمز، يجب عليك تحويلها إلى معايير حتى تتمكن من تتبع التغييرات فيها.

  • وبالتالي، مع تطوّر قاعدة الرموز البرمجية، يمكنكم معرفة ما إذا كانت المقاييس تتغيّر، وما إذا كانت لوحات البيانات تعكس تغييرات كبيرة، لأنّ ذلك يشير إلى حدوث تغيير مهم.

  • أو يمكنك تخطّي جميع الخطوات السابقة والطلب من وكيلك تنفيذها.

  • يمكنك استخدام هذه الإرشادات وتحويلها إلى مهارة، ثم توجيه وكيلك المفضّل إليها، وتركه ينفّذها.

  • أجل

الإعداد والملخّص والاستنتاج

  • راهول رافيكومار

    إذا أقنعتك بأنّ التتبُّع يستحق المحاولة وأنّه عليك تجربته، إليك كيفية إعداد المشروع.

  • الأمر بسيط جدًا.

  • التتبُّع هو مجرد مكتبة Java تقليدية بسيطة، لذا يمكنك تحديد تبعية على androidx.tracing:tracing-wire، وهو برنامج تشغيل التتبُّع المتوافق مع Perfetto بالإضافة إلى مجموعة التتبُّع، والإصدار الثابت الحالي هو 2.0.5.

  • إذا كنت تستخدم مكتبة Java، فالأمر نفسه تمامًا، فهذه واجهة برمجة تطبيقات متعددة الأنظمة الأساسية بالكامل.

  • ونريد توسيع نطاقها لتشمل منصات أخرى عندما يكون ذلك منطقيًا.

  • وأردتُ فقط تقديم ملخّص سريع.

  • لقد تحدّثتُ عن كل هذه الميزات الجديدة الرائعة التي أضفناها.

  • لذا، فإنّ ميزة "نشر السياق" فعّالة للغاية.

  • لدينا القدرة على تضمين معلومات مخصّصة وبيانات وصفية ومعلومات حزمة التنفيذ وجميع أنواع العناصر الأخرى.

  • لم أتحدث عن أشياء كثيرة.

  • يمكنك بالطبع تغيير تنسيق التتبُّع، وتغيير مصدر التتبُّع، ونقل بيانات التتبُّع إلى OpenTelemetry.

  • هذه هي كل الأشياء التي يمكنك القيام بها.

  • يمكن استخدام ميزة "التتبُّع" على أنظمة أساسية متعددة.

  • هذا أيضًا قرار واعٍ للغاية لأننا نريد أن يتمكن أكبر عدد ممكن من الأشخاص من الاستفادة من هذا.

  • وجميع الأدوات التي نقدّمها، سواء كانت Perfetto أو AndroidX Benchmarks أو أداة تحليل الأداء الجديدة أو Android Profiler القديمة، تتيح استخدام Tracing 2.0.

  • بهذا، أشكركم جزيل الشكر على الاستماع.

  • أتمنى لك قمة مطوّرين رائعة، وشكرًا لك.