نظرة عامة على العمليات وسلاسل المحادثات

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

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

يناقش هذا المستند آلية عمل العمليات وسلاسل المحادثات في تطبيق Android.

العمليات

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

يتيح إدخال البيان لكل نوع من أنواع العناصر المكوِّنة، <activity> و<service> و<receiver> و<provider>، استخدام السمة android:process التي يمكنها تحديد العملية التي يتم فيها تشغيل المكوِّن. يمكنك تعيين هذه السمة بحيث يتم تشغيل كل مكون في العملية الخاصة به أو بحيث تشترك بعض المكونات في العملية بينما لا يشترك البعض الآخر.

يمكنك أيضًا ضبط android:process بحيث تعمل مكوّنات التطبيقات المختلفة في العملية نفسها، بشرط أن تشترك التطبيقات في رقم تعريف مستخدم Linux نفسه وأن تكون موقَّعة باستخدام الشهادات نفسها.

يتيح العنصر <application> أيضًا استخدام السمة android:process التي يمكنك استخدامها لضبط قيمة تلقائية تنطبق على جميع المكوّنات.

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

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

وتتم مناقشة تفاصيل دورة حياة العملية وعلاقتها بحالات التطبيق في العمليات ومراحل نشاط التطبيق.

Threads

عند تشغيل تطبيق ما، ينشئ النظام سلسلة محادثات لتنفيذ التطبيق، تُسمى سلسلة التعليمات الرئيسية. إنّ سلسلة المحادثات هذه مهمة جدًا، لأنّها مسؤولة عن إرسال الأحداث إلى أدوات واجهة المستخدم المناسبة، بما في ذلك أحداث الرسم. وهي أيضًا تقريبًا سلسلة المحادثات التي يتفاعل فيها تطبيقك مع مكوّنات حِزمتَي android.widget وandroid.view في مجموعة أدوات واجهة المستخدم من Android. لهذا السبب، يُطلق على سلسلة التعليمات الرئيسية أحيانًا اسم سلسلة محادثات واجهة المستخدم. ومع ذلك، في ظل ظروف خاصة، قد لا تكون سلسلة التعليمات الرئيسية للتطبيق هي مؤشر ترابط واجهة المستخدم الخاص به. لمزيد من المعلومات، راجع التعليقات التوضيحية في Thread.

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

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

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

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

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

  1. لا تحظر سلسلة محادثات واجهة المستخدم.
  2. لا تصل إلى مجموعة أدوات واجهة المستخدم من Android من خارج سلسلة محادثات واجهة المستخدم.

سلاسل المحادثات للعاملين

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

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

يستخدم المثال التالي View.post(Runnable):

Kotlin

fun onClick(v: View) {
    Thread(Runnable {
        // A potentially time consuming task.
        val bitmap = processBitMap("image.png")
        imageView.post {
            imageView.setImageBitmap(bitmap)
        }
    }).start()
}

Java

public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            // A potentially time consuming task.
            final Bitmap bitmap =
                    processBitMap("image.png");
            imageView.post(new Runnable() {
                public void run() {
                    imageView.setImageBitmap(bitmap);
                }
            });
        }
    }).start();
}

هذا التنفيذ آمن من خلال سلاسل المحادثات لأنّ العملية في الخلفية تتم من سلسلة محادثات منفصلة، بينما يتم دائمًا التلاعب بـ ImageView من سلسلة محادثات واجهة المستخدم.

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

الطرق الآمنة من خلال سلسلة المحادثات

في بعض الحالات، يتم استدعاء الأساليب التي تنفذها من أكثر من سلسلة تعليمات، لذلك يجب كتابتها لتكون آمنة من سلسلة تعليمات.

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

على سبيل المثال، بينما يتم استدعاء طريقة onBind() لخدمة ما من سلسلة تعليمات واجهة المستخدم لعملية الخدمة، يتم استدعاء الطرق المنفَّذة في الكائن الذي يعرضه onBind()، مثل فئة فرعية تنفّذ طُرق استدعاء الإجراءات عن بُعد (RPC) من سلاسل المحادثات في مجموعة التجميع. ولأنّ الخدمة يمكن أن يكون لها أكثر من عميل واحد، يمكن لأكثر من سلسلة محادثات مجمّعة استخدام طريقة IBinder نفسها في الوقت نفسه، لذلك يجب تنفيذ طُرق IBinder لتكون آمنة من سلسلة محادثات.

وبالمثل، يمكن لموفر المحتوى تلقي طلبات البيانات التي تنشأ في عمليات أخرى. تُخفي الفئتان ContentResolver وContentProvider تفاصيل كيفية إدارة الاتصال البيني للعمليات (IPC)، ولكن يتم استدعاء طرق ContentProvider التي تستجيب لهذه الطلبات، وهي الطرق query() وinsert() وdelete() وupdate() وgetType()، من مجموعة من سلاسل التعليمات في عملية موفّر المحتوى، وليس من سلسلة تعليمات واجهة المستخدم. ولأنه قد يتم استدعاء هذه الطرق من أي عدد من سلاسل الرسائل في نفس الوقت، يجب تنفيذها أيضًا لتكون آمنة من سلسلة التعليمات.

التواصل بين العمليات

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

يتم بعد ذلك نقل القيم المرتجعة في الاتجاه المعاكس. يوفر Android كل الرمز لتنفيذ معاملات IPC هذه، حتى تتمكّن من التركيز على تحديد واجهة برمجة RPC وتنفيذها.

لإجراء IPC، يجب أن يرتبط تطبيقك بخدمة باستخدام bindService(). لمزيد من المعلومات، يُرجى الاطّلاع على نظرة عامة على الخدمات.