الكوروتينات (Coroutines) هي خيوط خفيفة في Kotlin للبرمجة غير المتزامنة، متاحة من خلال مكتبة kotlinx.coroutines. وفقاً لـ JetBrains Kotlin Documentation, 2026، Coroutines تسمح بتعليق تنفيذ الدالة دون حظر الخيط، على عكس Threads التقليدية. تعمل الكوروتينات على مجموعة محدودة من الخيوط، مما يجعلها أخف ألف مرة من الخيوط الأصلية. Kotlin Coroutines مدمجة بالكامل مع Android Jetpack و Retrofit و Room ومكتبات أخرى شهيرة في نظام Android البيئي.
النقاط الرئيسية
Coroutines هي آلية برمجة غير متزامنة في Kotlin، مطبقة في مكتبة kotlinx.coroutines. على عكس خيوط نظام التشغيل، الكوروتينات غير مرتبطة بخيط معين: يمكنها التوقف على خيط والاستئناف على خيط آخر. يمكن لخيط واحد تنفيذ آلاف الكوروتينات، بالتبديل بينها بأقل تكلفة إضافية.
ظهرت الكوروتينات في Kotlin 1.3 (2018) كميزة تجريبية وأصبحت مستقرة في Kotlin 1.5 (2021). Coroutines تحل مشكلة callback hell بطريقة مشابهة لـ async/await، ولكنها توفر API أكثر ثراءً: القنوات (Channel)، Flow، معالجة الاستثناءات في تسلسل Job الهرمي والتكامل المباشر مع Android Lifecycle.
وفقاً لـ JetBrains (2025)، تستهلك كل كوروتين حوالي 100 بايت من الذاكرة مقابل 1+ ميغابايت للخيط الأصلي. يتيح ذلك تشغيل ملايين الكوروتينات في تطبيق واحد دون خطر OutOfMemoryError. إن خفة الكوروتينات هي ما يجعلها الأداة المفضلة لعدم التزامن في Android.
يتم ترجمة كل كوروتين Kotlin إلى آلة حالة باستخدام Continuation Passing Style (CPS). يضيف المترجم معامل Continuation مخفياً لكل دالة suspend. يحتوي Continuation على نقطة الاستئناف وجميع المتغيرات المحلية. عندما يتوقف الكوروتين، يحفظ وقت التشغيل Continuation، وعندما يستأنف، يستعيده على أي خيط متاح من مجموعة Dispatcher.
suspend هي كلمة مفتاحية في Kotlin تميز الدالة كقابلة للتوقف. لا يمكن استدعاء هذه الدالة إلا من دالة suspend أخرى أو من كوروتين. داخل دالة suspend، يمكن استدعاء دوال suspend أخرى بأي ترتيب، وكل نقطة استدعاء هي نقطة توقف محتملة.
الآلية بسيطة: عندما تستدعي دالة suspend دالة suspend أخرى، فإنها تتوقف عند تلك النقطة، محررة الخيط. بعد اكتمال الدالة المستدعاة، يواصل وقت التشغيل التنفيذ من المكان المحفوظ. وهذا ما يسمى cooperative cancellation — لا يتم حظر أي خيوط.
مهم: دالة suspend ليست غير متزامنة افتراضياً. يبقى ترتيب التنفيذ تسلسلياً إذا لم يتم استخدام launch أو async. suspend تسمح فقط للدالة بالتوقف دون حظر الخيط وأن تكون جزءاً من سياق الكوروتين. Continuation Passing Style هو نموذج ترجمة حيث تستقبل كل دالة suspend استدعاء Continuation مخفياً، ويولد المترجم آلة حالة لإدارة حالات التوقف والاستئناف.
CoroutineScope هو سياق يحدد دورة حياة الكوروتينات. يجب تشغيل جميع الكوروتينات داخل scope. عندما يتم إلغاء scope (مثلاً عند انتهاء Activity)، يتم إلغاء جميع الكوروتينات التابعة له تلقائياً. يمنع هذا تسرب المهام الخلفية. Android Jetpack يوفر scopes جاهزة لكل مكون: viewModelScope لـ ViewModel و lifecycleScope لـ Activity و Fragment، والتي يتم إلغاؤها تلقائياً عند تدمير المكون المقابل.
Structured Concurrency هو مبدأ يضمن عدم اكتمال الكوروتين حتى تكتمل جميع الكوروتينات التابعة له. يشكل التسلسل الهرمي Job شجرة: كوروتين جذر ينشئ parent job، والأبناء ينشئون child jobs. إلغاء parent job ينتشر إلى جميع الأبناء. Structured Concurrency هو فرق أساسي بين الكوروتينات والخيوط.
| Scope | أين يُستخدم | الإلغاء |
|---|---|---|
| GlobalScope | للمهام الخلفية فقط | لا يُلغى تلقائياً |
| viewModelScope | Android ViewModel | عند مسح ViewModel |
| lifecycleScope | Android Activity/Fragment | عند تدمير lifecycle |
| coroutineScope | داخل دالة suspend | عند إلغاء job الأب |
Job العادي يلغي جميع siblings عند فشل كوروتين تابع. SupervisorJob استثناء: الفشل في كوروتين تابع لا يؤثر على البقية. هذا مهم عندما يتم تنفيذ عدة مهام مستقلة بالتوازي، وقد تتعطل إحداها دون الحاجة إلى إلغاء البقية.
Dispatchers تحدد الخيوط التي تنفذ الكوروتينات. Dispatchers.Main — خيط واجهة المستخدم الرئيسي في Android. Dispatchers.IO — مجموعة للعمليات الحاجبة (الشبكة، القرص). Dispatchers.Default — للمهام كثيفة الاستخدام للمعالج. Dispatchers.Unconfined — يبدأ في الخيط الحالي لكنه لا يضمن البقاء عليه. اختيار Dispatcher المناسب أمر بالغ الأهمية للأداء: مهمة IO على Default ستحجب مجموعة الحوسبة، بينما مهمة CPU على IO ستنشئ خيوطاً غير ضرورية.
withContext — دالة لتبديل Dispatcher داخل الكوروتين. على سبيل المثال، دالة suspend تقوم بتحليل JSON يمكنها التبديل إلى Dispatchers.Default للحوسبة والعودة إلى Dispatchers.Main لتحديث واجهة المستخدم. withContext هو أكثر باني استخداماً في تطوير Android.
launch — يشغل كوروتين، ويعيد Job، ولا يعيد نتيجة (fire-and-forget). async — يشغل كوروتين، ويعيد Deferred يمكن الحصول على النتيجة منه عبر await. runBlocking — يحظر الخيط الحالي لتنفيذ كوروتين (فقط للاختبارات ودوال main). اختيار الباني يعتمد على السيناريو: launch مناسب للأحداث والتحديثات، async للمهام ذات النتيجة، runBlocking فقط للاختبارات أو نقاط الدخول.
لننظر في ثلاثة سيناريوهات عملية: كوروتين أساسي مع launch، استدعاء متوازي مع async، ومعالجة الأخطاء مع SupervisorJob.
viewModelScope.launch يشغل كوروتين في سياق ViewModel. عند مسح ViewModel، يتم إلغاء الكوروتين تلقائياً.
class ProfileViewModel : ViewModel() {
fun loadUser() {
viewModelScope.launch(Dispatchers.IO) {
val user = api.fetchUser()
withContext(Dispatchers.Main) {
showUser(user)
}
}
}
}
coroutineScope مع async يشغل ثلاثة طلبات بالتوازي. تُجمع النتائج عبر .await(). إذا فشل أي طلب، تُلغى جميعها.
suspend fun loadDashboard(): Dashboard = coroutineScope {
val user = async { api.fetchUser() }
val posts = async { api.fetchPosts() }
val stats = async { api.fetchStats() }
Dashboard(user.await(), posts.await(), stats.await())
}
SupervisorJob يسمح لكل كوروتين بالاكتمال بشكل مستقل. الخطأ في طلب واحد لا يلغي البقية.
val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
scope.launch {
try { api.fetchUsers() } catch (e: Exception) { log(e) }
}
scope.launch {
try { api.fetchPosts() } catch (e: Exception) { log(e) }
}
الخيوط (Thread) هي بدائية نظام تشغيل. لكل خيط مكدس خاص به (~1 ميغابايت) ويتطلب استدعاء نظام للإنشاء والتبديل. الكوروتينات هي بدائية لغة، غير مرتبطة بنظام التشغيل. تستخدم Continuation لحفظ الحالة والتبديل على مستوى وقت التشغيل دون استدعاءات نظام.
وفقاً لـ Google (2025)، استخدام الكوروتينات بدلاً من الخيوط يقلل استهلاك الذاكرة للمهام الخلفية في تطبيقات Android بنسبة 90–95%. جميع مكتبات Android الحديثة (Retrofit, Room, WorkManager) لديها دعم مدمج للكوروتينات من خلال دوال suspend. Ktor (إطار عمل HTTP العميل من JetBrains) مبني بالكامل على الكوروتينات، ويوفر دوال suspend لكل طلب دون API للاستدعاءات. Room يدعم الكوروتينات من خلال دوال suspend في DAO، مما يسمح بتنفيذ استعلامات قاعدة البيانات دون حظر الخيط الرئيسي.
تظل الخيوط ضرورية لـ الكود الأصلي عبر JNI، والاستدعاءات الحاجبة كثيفة المعالجة دون حد زمني (تقديم الفيديو، المحاكاة) وعند التكامل مع مكتبات C. لكل شيء آخر — الكوروتينات.
الأسئلة الشائعة
الكوروتين هو وحدة عمل قابلة للتوقف يتم تنفيذها على خيط موجود. الخيط هو مورد نظام بمكدس خاص به. الكوروتينات أخف بآلاف المرات من الخيوط ولا تحظر الموارد عند التوقف.
Dispatchers.IO مصمم لعمليات الإدخال/الإخراج الحاجبة (الشبكة، الملفات) ويمكنه إنشاء خيوط جديدة عند الضرورة. Dispatchers.Default لديه مجموعة ثابتة الحجم (عدد أنوية المعالج) للحوسبات كثيفة المعالجة.
Job.cancel() يلغي الكوروتين وجميع أبنائه. للتحقق من الإلغاء داخل الكوروتين، استخدم ensureActive() — يطرح CancellationException إذا تم إلغاء الكوروتين.
نعم — من خلال مكتبة kotlinx-coroutines-rx3. توفر دوال awaitSingle و awaitFirst وغيرها لتحويل Observable/Single إلى دوال suspend والعودة عبر flowable.
Flow هو تدفق بيانات غير متزامن بارد، المكافئ في الكوروتينات لـ RxJava Observable. يصدر Flow القيم بالتسلسل وينتهي باستثناء أو نجاح. يدعم map و filter و catch وعوامل تشغيل أخرى.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا