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

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

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

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

العمليات

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

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

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

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

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

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

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

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

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

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

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

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

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

يُرجى العِلم أنّ مجموعة أدوات واجهة المستخدم من Android ليست آمنة للاستخدام المتزامن. لذلك، لا تعدّل واجهة المستخدم من سلسلة وحدات عاملة. يجب إجراء جميع عمليات المعالجة على واجهة المستخدم من سلسلة UI. هناك قاعدتان لنموذج Android ذي سلسلة المحادثات الفردية:

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

سلاسل الوحدات العاملة

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

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

توضّح الأمثلة التالية كيفية تخفيف الحِمل عن مهمة إلى سلسلة تعليمات في الخلفية وتعديل سلسلة واجهة المستخدم بعد اكتمال المهمة:

Kotlin

// Kotlin coroutines implementation.
fun onClick(v: View) {
    // Launch a coroutine in the lifecycle scope (e.g., in an Activity or Fragment).
    lifecycleScope.launch {
        // Run the blocking task on the IO dispatcher.
        val bitmap = withContext(Dispatchers.IO) {
            BitmapFactory.decodeFile("image.png")
        }
        // Back on the main thread, update the UI.
        imageView.setImageBitmap(bitmap)
    }
}

Java

// Java Executor implementation.
// (executorService is assumed to be defined elsewhere).
public void onClick(View v) {
    executorService.execute(() -> {
        // Run the heavy task on a background thread.
        Bitmap bitmap = BitmapFactory.decodeFile("image.png");

        // Update the View on the UI thread.
        imageView.post(() -> imageView.setImageBitmap(bitmap));
    });
}

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

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

طُرق آمنة للاستخدام المتزامن

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

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

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

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

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

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

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

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