تحسين الأداء من خلال سلاسل المحادثات

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

سلسلة التعليمات الرئيسية

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

الشؤون الداخلية

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

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

أثناء حدوث رسم متحرك أو تحديث للشاشة، يحاول النظام تنفيذ كتلة العمل (المسئولة عن رسم الشاشة) كل 16 ملّي ثانية أو نحو ذلك، ليتم العرض بسلاسة عند 60 لقطات في الثانية. ولكي يتمكّن النظام من تحقيق هذا الهدف، يجب استخدام التدرج الهرمي لواجهة المستخدم/العرض يجب تحديثه على سلسلة التعليمات الرئيسية. ومع ذلك، عندما تتضمن قائمة انتظار مراسلة سلسلة التعليمات الرئيسية يحتوي على مهام إما أن تكون كبيرة جدًا أو طويلة جدًا بحيث يتعذّر على سلسلة التعليمات الرئيسية إكمال التحديث بسرعة كافية، فيجب أن ينقل التطبيق هذا العمل إلى أحد الموظفين . إذا لم تتمكن سلسلة التعليمات الرئيسية من إنهاء تنفيذ كتل العمل خلال 16 ملي ثانية، قد يلاحظ المستخدم قطعًا أو تأخرًا أو نقصًا في استجابة واجهة المستخدم للمدخلات. إذا كانت سلسلة التعليمات الرئيسية تحظر المحتوى لمدة خمس ثوانٍ تقريبًا، سيعرض النظام التطبيق مربّع حوار "عدم الاستجابة" (ANR)، ما يسمح للمستخدم بإغلاق التطبيق مباشرةً

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

سلاسل المحادثات ومراجع كائنات واجهة المستخدم

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

تنقسم المشاكل المتعلقة بالمراجع إلى فئتَين مختلفتَين: المراجع الفاضحة. والمراجع الضمنية.

المراجع الفاضحة

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

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

في مثال آخر، تحتوي عناصر View على إشارات إلى النشاط. التي تمتلكها. في حال حذف يتم تدمير هذا النشاط، ولكن لا يزال هناك جزء من العمل المترابط التي تشير إليها — بشكل مباشر أو غير مباشر — فلن يجمع جهاز تجميع البيانات المهملة النشاط حتى يتم الانتهاء من تنفيذ كتلة العمل هذه.

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

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

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

المراجع الضمنية

يمكن رؤية خلل شائع في تصميم التعليمات البرمجية مع الكائنات المتسلسلة في مقتطف الرمز أدناه:

Kotlin

class MainActivity : Activity() {
    // ...
    inner class MyAsyncTask : AsyncTask<Unit, Unit, String>() {
        override fun doInBackground(vararg params: Unit): String {...}
        override fun onPostExecute(result: String) {...}
    }
}

Java

public class MainActivity extends Activity {
  // ...
  public class MyAsyncTask extends AsyncTask<Void, Void, String>   {
    @Override protected String doInBackground(Void... params) {...}
    @Override protected void onPostExecute(String result) {...}
  }
}

يكمن العيب في هذا المقتطف في أنّ الرمز يعلن عن كائن سلسلة المحادثات. MyAsyncTask كفئة داخلية غير ثابتة لبعض الأنشطة (أو فئة داخلية في Kotlin). ينشئ هذا البيان إشارة ضمنية إلى السمة Activity المضمّنة. مثال. ونتيجةً لذلك، يحتوي الكائن على مرجع للنشاط حتى ويحدث العمل المترابط، ما يؤدي إلى تأخير في إتلاف النشاط المُشار إليه. وهذا التأخير بدوره يزيد من الضغط على الذاكرة.

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

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

مراحل نشاط سلاسل المحادثات والأنشطة على التطبيقات

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

سلاسل المحادثات الثابتة

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

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

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

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

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

أولوية سلسلة المحادثات

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

في كل مرة تنشئ فيها سلسلة محادثات، عليك الاتصال setThreadPriority() سلسلة محادثات النظام التي تعطي الأولوية لسلاسل المحادثات ذات الأولوية العالية، مع موازنة تلك السلاسل الأولويات مع الحاجة إلى إنجاز جميع الأعمال في النهاية. بشكل عام، سلاسل المحادثات في المقدمة حوالي 95% من إجمالي وقت التنفيذ من الجهاز، في حين أن مجموعة الخلفية حوالي 5٪.

ويعيّن النظام أيضًا قيمة الأولوية الخاصة لكل سلسلة محادثات، وذلك باستخدام صف واحد (Process).

يضبط النظام تلقائيًا أولوية سلسلة المحادثات على الأولوية والمجموعة نفسها. العضوية في القنوات ومع ذلك، يمكن لتطبيقك اضبط أولوية سلسلة المحادثات باستخدام setThreadPriority()

Process في تقليل التعقيد في تعيين قيم الأولوية من خلال توفير من الثوابت التي يمكن لتطبيقك استخدامها لتحديد أولويات سلاسل المحادثات. على سبيل المثال: THREAD_PRIORITY_DEFAULT ويمثل القيمة الافتراضية لسلسلة محادثات. يجب أن يضبط تطبيقك أولوية سلسلة المحادثات على. THREAD_PRIORITY_BACKGROUND لسلاسل المحادثات التي تنفّذ إجراءات أقل إلحاحًا

يمكن لتطبيقك استخدام THREAD_PRIORITY_LESS_FAVORABLE. وTHREAD_PRIORITY_MORE_FAVORABLE الثوابت كعوامل زيادة لتعيين الأولويات النسبية. للحصول على قائمة أولويات سلسلة المحادثات، انظر THREAD_PRIORITY ثوابت في للفئة Process.

لمزيد من المعلومات حول إدارة سلاسل المحادثات، فراجع الوثائق المرجعية حول Thread وProcess صفًا.

فئات المساعدة لسلاسل المحادثات

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

يوفر إطار العمل أيضًا نفس فئات وأساسيات Java لتسهيل سلاسل المحادثات، مثل Thread وRunnable ، وExecutors صفًا، بالإضافة إلى علامات أخرى مثل HandlerThread. لمزيد من المعلومات، يُرجى الاطّلاع على مقالة Threading على Android.

فئة HandlerThread

سلسلة المعالجات هي سلسلة تعليمات طويلة الأمد تستخرج العمل من قائمة الانتظار وتعمل عليها.

فكّر في تحدٍ شائعٍ يتمثل في الحصول على إطارات معاينة من عنصر Camera. عند التسجيل في إطارات معاينة "الكاميرا"، ستظهر لك في onPreviewFrame() عملية الاسترداد، التي يتم استدعاؤها في سلسلة أحداث الحدث التي تم استدعاؤها منها. إذا كان هذا في سلسلة واجهة المستخدم، وهي مهمة التعامل مع وحدة البكسل الضخم مع عمل العرض ومعالجة الأحداث.

في هذا المثال، عندما يفوّض تطبيقك الأمر Camera.open() إلى كتلة عمل في مؤشر ترابط المعالِج، والأجزاء المرتبطة onPreviewFrame() رد الاتصال إلى سلسلة المعالج، بدلاً من مؤشر ترابط واجهة المستخدم. لذا، إذا كنت تنوي تنفيذ أهداف طويلة تعمل على وحدات البكسل، فقد يكون هذا حلاً أفضل لك.

عندما ينشئ تطبيقك سلسلة محادثات باستخدام "HandlerThread"، نسيان ضبط حسب الأولوية بناءً على نوع العمل الذي يؤديه. تذكر أن وحدات المعالجة المركزية (CPU) يمكنها التعامل مع عدد صغير من السلاسل بشكل متوازٍ. يساعد تحديد الأولوية يعرف النظام الطرق الصحيحة لجدولة هذا العمل عندما تكون جميع سلاسل التعليمات الأخرى يكافحون من أجل جذب الانتباه.

فئة ThreadPoolExecutor

هناك أنواع معينة من العمل يمكن اختصارها لتكون متوازية للغاية، المهام الموزعة. على سبيل المثال، تتمثل إحدى هذه المهام في حساب عامل تصفية لكل قالب 8×8 لصورة بدقة 8 ميغابكسل. مع الحجم الهائل لحزم العمل التي يتم إنشاؤه، فإن HandlerThread ليست الفئة المناسبة للاستخدام.

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

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

كم عدد سلاسل المحادثات التي يجب إنشاؤها؟

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

من الناحية العملية، هناك عدد من المتغيرات المسئولة عن ذلك، لكن واختيار قيمة (مثل 4، للمبتدئين)، واختبارها باستخدام Systrace هو وضع استراتيجية مثل أي استراتيجية أخرى. يمكنك استخدام الطريقة التجريبية والخطأ لاكتشاف والحد الأدنى لعدد سلاسل المحادثات التي يمكنك استخدامها بدون مواجهة أي مشاكل.

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

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