وقت بدء تشغيل التطبيق

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

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

فهم حالات بدء تشغيل التطبيق المختلفة

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

ننصحك دائمًا بإجراء التحسينات استنادًا إلى افتراض بدء التشغيل من حالة التوقف التام. ويمكن أن يؤدي ذلك إلى تحسين أداء عمليات بدء التشغيل السريع والتشغيل السريع جدًا أيضًا.

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

هناك مقياسان مهمان لتحديد مدة بدء تشغيل التطبيق، وهما الوقت المستغرَق للعرض الأولي (TTID) والوقت المستغرَق للعرض الكامل (TTFD). وTTID هو الوقت المستغرَق لعرض الإطار الأول، وTTFD هو الوقت المستغرَق لكي يصبح التطبيق تفاعليًا بالكامل. وكلاهما مهمّان على حدّ سواء، لأنّ وقت بدء عرض المحتوى يتيح للمستخدم معرفة أنّ التطبيق قيد التحميل، ووقت عرض المحتوى هو الوقت الذي يصبح فيه التطبيق قابلاً للاستخدام. إذا كان أي من هذين المقياسين طويلاً جدًا، قد يخرج المستخدم من تطبيقك قبل تحميله بالكامل.

التشغيل من وضع غير نشط

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

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

في بداية التشغيل على البارد، ينفِّذ النظام المهام الثلاث التالية:

  1. حمِّل التطبيق وأطلقه.
  2. عرض نافذة بدء فارغة للتطبيق بعد تشغيله مباشرةً
  3. أنشئ عملية التطبيق.

بعد أن ينشئ النظام عملية التطبيق، تتحمّل عملية التطبيق مسؤولية المراحل التالية:

  1. أنشئ عنصر التطبيق.
  2. ابدأ سلسلة التعليمات الرئيسية.
  3. أنشئ النشاط الرئيسي.
  4. زيادة عدد المشاهدات بشكل زائف
  5. تنسيق الشاشة
  6. نفِّذ عملية السحب الأولية.

عندما تُكمل عملية التطبيق عملية الرسم الأولى، تستبدل عملية النظام نافذة الخلفية المعروضة، وتستبدلها بالنشاط الرئيسي. في هذه المرحلة، يمكن للمستخدم بدء استخدام التطبيق.

يوضّح الشكل 1 كيفية تسليم عمليات النظام والتطبيق للمهام بعضها إلى بعض.

الشكل 1. تمثيل مرئي للأجزاء المهمة من إطلاق التطبيق للمرة الأولى

يمكن أن تحدث مشاكل في الأداء أثناء إنشاء التطبيق وإنشاء النشاط.

إنشاء التطبيق

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

في حال إلغاء Application.onCreate() في تطبيقك، يبدأ النظام في استدعاء طريقة onCreate() في عنصر تطبيقك. بعد ذلك، ينشئ التطبيق سلسلة التعليمات الرئيسية، المعروفة أيضًا باسم سلسلة تعليمات واجهة المستخدم، ويفوّضها بإنشاء نشاطك الرئيسي.

من هذه النقطة، تستمر العمليات على مستوى النظام والتطبيق وفقًا ل مراحل دورة حياة التطبيق.

إنشاء النشاط

بعد أن يُنشئ التطبيق نشاطك، ينفّذ النشاط العمليات التالية:

  1. لإعداد القيم
  2. تُستخدَم لطلب إنشاء كائنات.
  3. تستدعي طريقة طلب معاودة الاتصال، مثل Activity.onCreate()، المناسبة لحالة دورة النشاط الحالية.

عادةً ما يكون لطريقة onCreate() أكبر تأثير في وقت التحميل، لأنّها تُنفِّذ العمل بأكبر قدر من التكاليف غير الضرورية: تحميل وتوسيع طرق العرض وإعداد الكائنات اللازمة لتشغيل النشاط.

إعادة التشغيل البطيء

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

  • يخرج المستخدم من تطبيقك ثم يعيد تشغيله. قد تستمر العملية في التقدّم، ولكن على التطبيق إعادة إنشاء النشاط من البداية باستخدام onCreate().

  • يُخرج النظام تطبيقك من الذاكرة، ثم يُعيد المستخدم تشغيله. يجب إعادة تشغيل المعالجة والنشاط، ولكن يمكن أن تستفيد المهمة إلى حد ما من حِزمة حالة المثيل المحفوظة التي تم تمريرها إلى onCreate().

التشغيل النشط

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

ومع ذلك، إذا تم محو بعض الذاكرة استجابةً لأحداث خفض الذاكرة، مثل onTrimMemory()، يجب إعادة إنشاء هذه العناصر استجابةً لحدث البدء السريع.

يعرض سيناريو إعادة التشغيل السريع السلوك نفسه على الشاشة مثل سيناريو التشغيل على البارد. تعرِض عملية النظام شاشة فارغة إلى أن ينتهي التطبيق من عرض المحتوى المرتبط بالنشاط.

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

كيفية تحديد وقت بدء تشغيل التطبيق في Perfetto

لتصحيح أخطاء مشاكل بدء تشغيل التطبيق، من المفيد تحديد ما الذي يتم تضمينه بالضبط في مرحلة بدء تشغيل التطبيق. لتحديد مرحلة بدء تشغيل التطبيق بالكامل في Perfetto، اتّبِع الخطوات التالية:

  1. في Perfetto، ابحث عن الصف الذي يتضمّن المقياس المستنِد إلى "عمليات بدء تشغيل تطبيقات Android". إذا لم يظهر لك، حاوِل تسجيل تتبع باستخدام تطبيق التتبّع على الجهاز للنظام.

    الشكل 3: شريحة المقياس المستمَدة من عمليات بدء تشغيل تطبيقات Android في IDE Perfetto.
  2. انقر على الشريحة المرتبطة واضغط على m لاختيار الشريحة. تظهر الأقواس حول الشريحة وتشير إلى المدة التي استغرقتها. يتم عرض المدة أيضًا في علامة التبويب الاختيار الحالي.

  3. ثبِّت صف "عمليات بدء تشغيل تطبيقات Android" من خلال النقر على رمز التثبيت الذي يظهر عند وضع المؤشر فوق الصف.

  4. انتقِل إلى الصف الذي يتضمّن التطبيق المعنيّ وانقر على الخلية الأولى لتوسيع الصف.

  5. تكبير المحادثة الرئيسية، التي تظهر عادةً في أعلى الشاشة، من خلال الضغط على w (اضغط على s وa وd لتصغير المحادثة والانتقال لليسار واليمين، على التوالي).

    الشكل 4: شريحة المقياس المستمَدة من عمليات بدء تشغيل تطبيقات Android بجانب السلسلة الرئيسية للتطبيق
  6. تسهّل شريحة المقاييس المستمدة الاطّلاع على ما يُدرَج بالضبط في عملية بدء تشغيل التطبيق، ما يتيح لك مواصلة تصحيح الأخطاء بمزيد من التفاصيل.

استخدام المقاييس لفحص التطبيقات الناشئة وتحسينها

لتشخيص أداء وقت بدء التشغيل بشكلٍ صحيح، يمكنك تتبُّع المقاييس التي تعرِض المدّة التي يستغرقها تطبيقك لبدء التشغيل. يوفّر نظام التشغيل Android عدة وسائل لإعلامك بأنّ تطبيقك يواجه مشكلة ومساعدتك في تشخيصها. يمكن أن تُنبّهك "مؤشرات Android الحيوية" بحدوث مشكلة، ويمكن أن تساعدك أدوات التشخيص في تشخيص المشكلة.

مزايا استخدام مقاييس بدء التشغيل

يستخدم نظام التشغيل Android مقياسَي الوقت المستغرَق للعرض الأولي (TTID) والوقت المستغرَق للعرض الكامل (TTFD) لتحسين عمليات بدء تشغيل التطبيقات للمرة الأولى وللمرة الثانية. يستخدم Android Runtime (ART) البيانات من هذه المقاييس لتجميع الرموز البرمجية مسبقًا بكفاءة بهدف تحسين عمليات التشغيل الأولى للتطبيقات المستقبلية.

تؤدي عمليات البدء الأسرع إلى تفاعل المستخدِم بشكلٍ أكثر استدامة مع تطبيقك، ما يؤدي بدوره إلى تقليل حالات الخروج المبكر أو إعادة تشغيل المثيل أو الانتقال إلى تطبيق مختلف.

مؤشرات Android الحيوية

يمكن أن تساعدك "مؤشرات Android الحيوية" في تحسين أداء تطبيقك من خلال تنبيهك على Play Console عندما تكون أوقات بدء تشغيل تطبيقك طويلة جدًا.

تُعتبر أوقات بدء تشغيل تطبيقك التالية مفرطة في "مؤشرات Android الحيوية":

  • يستغرق بدء التشغيل على البارد 5 ثوانٍ أو أكثر.
  • يستغرق بدء التشغيل البطيء ثانيتَين أو أكثر.
  • يستغرق بدء التشغيل السريع 1.5 ثانية أو أكثر.

تستخدِم "مؤشرات Android الحيوية" مقياس الوقت المستغرَق للعرض الأوّلي (TTID). للحصول على معلومات عن كيفية جمع Google Play لبيانات "مؤشرات Android الحيوية"، يُرجى الاطّلاع على مستندات Play Console.

الوقت المستغرَق للعرض الأولي

وقت العرض الأولي (TTID) هو الوقت الذي يستغرقه عرض الإطار الأول لواجهة مستخدم التطبيق. يقيس هذا المقياس الوقت الذي يستغرقه التطبيق لإنشاء لقطة الشاشة الأولى، بما في ذلك بدء العملية أثناء التشغيل من البارد وإنشاء النشاط أثناء التشغيل من البارد أو الدافئ وعرض لقطة الشاشة الأولى. يساعد إبقاء وقت بدء تشغيل تطبيقك منخفضًا في تحسين تجربة المستخدم من خلال السماح للمستخدمين بمشاهدة بدء تشغيل تطبيقك بسرعة. يتم تسجيل رقم TTID تلقائيًا لكل تطبيق من خلال "إطار عمل Android". عند تحسين سرعة تشغيل التطبيق، ننصحك بتنفيذ reportFullyDrawn للحصول على معلومات تصل إلى TTFD.

يتم قياس مقياس TTID كقيمة زمنية تمثّل إجمالي الوقت المنقضي الذي يتضمّن تسلسل الأحداث التالي:

  • بدء العملية
  • بدء تشغيل العناصر
  • إنشاء النشاط وإعداده
  • تضخيم التنسيق
  • رسم التطبيق لأول مرة

استرداد TTID

للعثور على TTID، ابحث في أداة سطر الأوامر Logcat عن سطر ناتج يحتوي على قيمة تُسمى Displayed. هذه القيمة هي TTID وتشبه المثال التالي الذي تكون فيه قيمة TTID هي 3s534ms:

ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms

للعثور على TTID في "استوديو Android"، أوقِف الفلاتر في عرض Logcat من القائمة المنسدلة filter، ثم ابحث عن وقت Displayed، كما هو موضّح في الشكل 5. من الضروري إيقاف الفلاتر لأنّ خادم النظام، وليس التطبيق نفسه، يعرض هذا السجلّ.

الشكل 5. الفلاتر غير المفعّلة وقيمة Displayed في logcat

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

في بعض الأحيان، يحتوي سطر Displayed في إخراج Logcat على حقل إضافي للوقت الإجمالي. مثلاً:

ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms (total +1m22s643ms)

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

ننصحك باستخدام Logcat في Android Studio، ولكن إذا لم تكن تستخدم Android Studio، يمكنك أيضًا قياس TTID من خلال تشغيل تطبيقك باستخدام adb shell command manager activity. وفي ما يلي مثال لذلك:

adb [-d|-e|-s <serialNumber>] shell am start -S -W
com.example.app/.MainActivity
-c android.intent.category.LAUNCHER
-a android.intent.action.MAIN

يظهر مقياس Displayed في ناتج Logcat كما كان من قبل. تعرض نافذة المحطة الطرفية ما يلي:

Starting: Intent
Activity: com.example.app/.MainActivity
ThisTime: 2044
TotalTime: 2044
WaitTime: 2054
Complete

الوسيطات -c و-a اختياريتان وتتيح لك تحديد <category> و<action>.

وقت ظهور الإعلان على الشاشة بالكامل

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

يحدِّد النظام رقم TTID عندما يُطلِق Choreographer طريقة onDraw() للنشاط، وعندما يعلم أنّه يُطلِق هذه الطريقة للمرة الأولى. ومع ذلك، لا يعرف النظام متى يجب تحديد وقت الاستجابة للطلبات لأنّ كل تطبيق يتصرف بشكل مختلف. لتحديد وقت عرض الصفحة، يجب أن يُرسِل التطبيق إشارة إلى النظام عند وصوله إلى حالة الرسم الكامل.

استرداد TTFD

للعثور على وقت عرض اللوحة، أرسِل إشارة إلى الحالة التي تم فيها الانتهاء من الرسم من خلال استدعاء الأسلوب reportFullyDrawn() في ComponentActivity. تُبلغ طريقة reportFullyDrawn عندما يكون التطبيق مرسومًا بالكامل وفي حالة صالحة للاستخدام. ومدة TTFD هي الوقت المستغرَق منذ تلقّي النظام لمحاولة بدء تشغيل التطبيق إلى وقت استدعاء reportFullyDrawn(). إذا لم يتم استدعاء reportFullyDrawn()، لن يتم تسجيل أي قيمة TTFD.

لقياس وقت الاستجابة للصفحة، اتصل بـ reportFullyDrawn() بعد الانتهاء من رسم واجهة المستخدم بالكامل وجميع البيانات. لا تستدعي reportFullyDrawn() قبل رسم نافذة النشاط الأول وعرضها أولاً حسب قياس النظام، لأنّه بعد ذلك يُبلِغ النظام بالوقت الذي قياسه. بعبارة أخرى، إذا استدعت reportFullyDrawn() قبل أن يرصد النظام TTID، يُبلغ النظام عن كلاً من TTID وTTFD على أنّهما القيمة نفسها، وهذه القيمة هي قيمة TTID.

عند استخدام reportFullyDrawn()، يعرض Logcat نتيجة مماثلة للمثال التالي ، حيث تكون مدة TTFD هي 1 ثانية و54 ملي ثانية:

system_process I/ActivityManager: Fully drawn {package}/.MainActivity: +1s54ms

يتضمّن ناتج Logcat أحيانًا وقت total، كما هو موضّح في مدّة الانتظار لبدء الظهور.

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

يمكنك استخدام reportFullyDrawn() للإشارة إلى الحالة المرسومة بالكامل في الحالات الأساسية التي تكون فيها على دراية بتحقيق الحالة المرسومة بالكامل. ومع ذلك، في الحالات التي يجب فيها أن تكمل مؤشرات الترابط في الخلفية العمل في الخلفية قبل اكتمال الحالة المرسومة بالكامل، عليك تأخير reportFullyDrawn() للحصول على قياس أدق لوقت عرض الصفحة. لمعرفة كيفية تأخير reportFullyDrawn()، يُرجى الاطّلاع على القسم التالي.

تحسين دقة توقيت بدء التشغيل

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

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

لتضمين عدد عناصر القائمة كجزء من توقيت مقياس الأداء، يمكنك الحصول على FullyDrawnReporter باستخدام getFullyDrawnReporter()، وإضافة مُبلِّغ إليه في رمز تطبيقك. اترك المُبلِّغ بعد أن تنتهي مهمة الخلفية من تعبئة القائمة.

لا تستدعي FullyDrawnReporter طريقة reportFullyDrawn() إلى أن يتم تحرير جميع المُبلِّغين المُضافين. من خلال إضافة مُبلِّغ إلى أن تكتمل العملية التي تعمل في الخلفية، تشمل التوقيتات أيضًا الوقت الذي يستغرقه ملء القائمة في بيانات توقيت بدء التشغيل. لا يؤدي ذلك إلى تغيير سلوك التطبيق بالنسبة إلى المستخدِم، ولكنه يسمح لبيانات توقيت بدء التشغيل بتضمين الوقت الذي يستغرقه ملء القائمة. لا يتمّ استدعاء reportFullyDrawn() إلى أن تكتمل جميع المهام، بغض النظر عن الترتيب.

يوضّح المثال التالي كيفية تنفيذ مهام متعددة في الخلفية بالتزامن، مع تسجيل كل منها لمسؤول إعداد التقارير الخاص به:

Kotlin

class MainActivity : ComponentActivity() {

    sealed interface ActivityState {
        data object LOADING : ActivityState
        data object LOADED : ActivityState
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            var activityState by remember {
                mutableStateOf(ActivityState.LOADING as ActivityState)
            }
            fullyDrawnReporter.addOnReportDrawnListener {
                activityState = ActivityState.LOADED
            }
            ReportFullyDrawnTheme {
                when(activityState) {
                    is ActivityState.LOADING -> {
                        // Display the loading UI.
                    }
                    is ActivityState.LOADED -> {
                        // Display the full UI.
                    }
                }
            }
            SideEffect {
                fullyDrawnReporter.addReporter()
                lifecycleScope.launch(Dispatchers.IO) {
                    // Perform the background operation.
                    fullyDrawnReporter.removeReporter()
                }
                fullyDrawnReporter.addReporter()
                lifecycleScope.launch(Dispatchers.IO) {
                    // Perform the background operation.
                    fullyDrawnReporter.removeReporter()
                }
            }
        }
    }
}

Java

public class MainActivity extends ComponentActivity {
    private FullyDrawnReporter fullyDrawnReporter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        fullyDrawnReporter = getFullyDrawnReporter();
        fullyDrawnReporter.addOnReportDrawnListener(() -> {
            // Trigger the UI update.
            return Unit.INSTANCE;
        });

        new Thread(new Runnable() {
            @Override
            public void run() {
                fullyDrawnReporter.addReporter();
                // Do the background work.
                fullyDrawnReporter.removeReporter();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                fullyDrawnReporter.addReporter();
                // Do the background work.
                fullyDrawnReporter.removeReporter();
            }
        }).start();
    }
}

إذا كان تطبيقك يستخدم Jetpack Compose، يمكنك استخدام واجهات برمجة التطبيقات التالية للإشارة إلى حالة الرسم بالكامل:

  • ReportDrawn: يشير ذلك إلى أنّ العنصر القابل للتجميع جاهز على الفور للتفاعل معه.
  • ReportDrawnWhen: تأخذ العنصر النائب، مثل list.count > 0، لتحديد ما إذا كان العنصر القابل للتجميع جاهزًا للتفاعل.
  • ReportDrawnAfter: تأخذ طريقة تعليق تشير عند اكتمالها إلى أنّ العنصر القابل للتجميع جاهز للتفاعل.
تحديد نقاط الضعف

للبحث عن نقاط الاختناق، يمكنك استخدام أداة تحليل وحدة المعالجة المركزية (CPU) في "استوديو Android". لمزيد من المعلومات، يُرجى الاطّلاع على مقالة فحص نشاط وحدة المعالجة المركزية باستخدام أداة تحليل وحدة المعالجة المركزية.

يمكنك أيضًا الحصول على إحصاءات عن نقاط الاختناق المحتمَلة من خلال التتبّع المضمّن داخل طرق onCreate() لتطبيقاتك وأنشطتك. للتعرّف على ميزة التتبُّع المضمّنة، اطّلِع على مستندات دوال Trace ونظرة عامة على تتبُّع النظام.

حلّ المشاكل الشائعة

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

بدء تشغيل التطبيق ببطء

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

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

تشمل التحديات الأخرى أثناء بدء تشغيل التطبيق أحداث جمع المهملات التي تكون مؤثرة أو كثيرة، أو عمليات I/O على القرص التي تحدث بشكل متزامن مع البدء، ما يؤدي إلى حظر عملية البدء بشكل أكبر. يجب مراعاة عملية جمع القمامة بشكل خاص في وقت تشغيل Dalvik، حيث يُجري Android IDE (ART) عملية جمع القمامة بشكل متزامن، ما يقلل من تأثير هذه العملية.

تشخيص المشكلة

يمكنك استخدام ميزة تتبُّع الطريقة أو التتبُّع المضمّن لمحاولة تشخيص المشكلة.

تتبُّع الطريقة

يكشف تشغيل أداة تحليل وحدة المعالجة المركزية أنّ الطريقة callApplicationOnCreate() تستدعي في النهاية الطريقة com.example.customApplication.onCreate. إذا كانت الأداة تُظهر أنّ هذه الطرق تستغرق وقتًا طويلاً لإكمال التنفيذ، يمكنك استكشاف المزيد لمعرفة نوع العمل الذي يتم تنفيذه.

التتبّع المضمّن

استخدِم التتبُّع المضمّن للتحقيق في الأسباب المحتمَلة، بما في ذلك ما يلي:

  • وظيفة onCreate() الأولية لتطبيقك.
  • أيّ عناصر فردية عالمية يُنشئها تطبيقك
  • أي عمليات إدخال/إخراج على القرص أو عمليات إلغاء تسلسل أو حلقات ضيقة قد تحدث أثناء الزحمة

حلول المشكلة

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

ننصحك أيضًا باستخدام إطار عمل لإدخال التبعيات، مثل Hilt الذي يُنشئ العناصر والتبعيات عند إدخالها لأول مرة.

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

بدء نشاط كثيف

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

  • تضخيم التنسيقات الكبيرة أو المعقّدة
  • حظر رسم الشاشة على القرص أو إدخال/إخراج الشبكة
  • تحميل ملفات الأشكال الممسوحة ضوئيًا وفك ترميزها
  • تحويل عناصر VectorDrawable إلى صور نقطية
  • بدء الأنظمة الفرعية الأخرى للنشاط

تشخيص المشكلة

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

تتبُّع الطريقة

عند استخدام "أداة تحليل وحدة المعالجة المركزية"، انتبه إلى Application طرق إنشاء الفئات الفرعية وcom.example.customApplication.onCreate() في تطبيقك.

إذا أظهرت الأداة أنّ هذه الطرق تستغرق وقتًا طويلاً لإكمال التنفيذ، يمكنك استكشاف المزيد لمعرفة نوع العمل الذي يتم تنفيذه.

التتبّع المضمّن

استخدِم التتبّع المضمّن للتحقيق في الأسباب المحتمَلة، بما في ذلك ما يلي:

  • دالة onCreate() الأولية لتطبيقك.
  • أيّ عناصر فردية عالمية يتمّ إعدادها
  • أي عمليات إدخال/إخراج على القرص أو عمليات إلغاء تسلسل أو حلقات ضيقة قد تحدث أثناء الزحمة

حلول المشكلة

هناك العديد من نقاط الضعف المحتملة، ولكن هناك مشكلتان شائعتان وحلّان متعلّقان بهما يليان:

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

شاشات البداية المخصّصة

قد تلاحظ إضافة وقت إضافي أثناء بدء التشغيل إذا كنت قد استخدمت في السابق إحدى الطرق التالية لتنفيذ شاشة بداية مخصّصة في Android 11 (المستوى 30 من واجهة برمجة التطبيقات) أو إصدار أقدم:

  • استخدام سمة المظهر windowDisablePreview لإيقاف الشاشة الفارغة المبدئية التي يرسمها النظام أثناء الإطلاق
  • استخدام Activity مخصّص

اعتبارًا من الإصدار 12 من Android، يجب نقل البيانات إلى واجهة برمجة تطبيقات SplashScreen. تتيح لك واجهة برمجة التطبيقات هذه بدء التشغيل بشكل أسرع وتتيح لك تعديل شاشة البداية بالطرق التالية:

بالإضافة إلى ذلك، تُعيد مكتبة التوافق استخدام واجهة برمجة التطبيقات SplashScreen لتفعيل التوافق مع الإصدارات القديمة وإنشاء مظهر وأسلوب متسقَين لعرض شاشة التمهيد على جميع إصدارات Android.

راجِع دليل نقل بيانات شاشة البداية للاطّلاع على التفاصيل.