Coroutines والدوال المعلّقة
اكتبوا تعليمات برمجية غير متزامنة تبدو كأنها متزامنة. استخدموا الدوال المعلّقة ونطاقات coroutines وdispatchers (IO وMain وDefault).
Coroutines والدوال المعلّقة درس مجاني في Android Academy على CoddyKit. هذا هو الدرس 3 من أصل 6. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Android Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Android Academy 6 دروس في المجموع.
المشكلة: حظر خيط واجهة المستخدم
يشغّل Android جميع أعمال واجهة المستخدم على الخيط الرئيسي. إذا أجريت طلبًا شبكيًا أو قرأت من قاعدة البيانات على الخيط الرئيسي، تتجمّد واجهة المستخدم حتى انتهاء العملية.
إذا تجمّدت واجهة المستخدم لأكثر من 5 ثوانٍ، يعرض Android مربع حوار ANR (App Not Responding)، ويمكن للمستخدم فرض إغلاق التطبيق.
ما هي Coroutines؟
تُعد Kotlin Coroutines أسلوبًا لكتابة شيفرة غير متزامنة تبدو تسلسلية، من دون callbacks أو سلاسل RxJava.
- خفيفة الوزن، إذ يمكن تشغيل آلاف coroutines بالتزامن
- قابلة للتعليق، إذ يمكن لـ coroutine التوقف والاستئناف من دون حظر خيط
- منظمة، إذ تحترم دورة حياة محددة (نطاقًا)
دوال suspend
يمكن لدالة تحمل الوسم suspend إيقاف تنفيذها مؤقتًا من دون حظر الخيط. ولا يمكن استدعاؤها إلا من دالة suspend أخرى أو من نطاق coroutine.
import kotlinx.coroutines.*
suspend fun fetchData(): String {
delay(1000) // simulates 1 second network call
return "Data loaded!"
}
fun main() = runBlocking {
println("Fetching...")
val result = fetchData() // suspends here, doesn't block
println(result) // Data loaded!
println("Done")
}نطاقات Coroutine
تعمل كل coroutine داخل نطاق يحدد دورة حياتها:
viewModelScope— تُلغى عند إزالة ViewModellifecycleScope— تُلغى عند تدمير Activity أو FragmentGlobalScope— تستمر إلى الأبد، لذا تجنّبهاrunBlocking— للاختبار فقط
مقارنة launch وasync
طريقتان لبدء coroutine:
launch { }— تشغيل دون انتظار، ولا تُعيد قيمةasync { }.await()— تُعيد نتيجة يمكنك انتظارها
import kotlinx.coroutines.*
fun main() = runBlocking {
// launch: no return value
launch {
delay(500)
println("launch done")
}
// async: returns a Deferred<T>
val deferred = async {
delay(300)
42
}
val result = deferred.await()
println("async result: $result")
}الموزّعات: أي خيط؟
تتحكم Dispatchers في الخيط أو الخيوط التي تعمل عليها coroutine:
Dispatchers.Main— خيط واجهة المستخدم (لتحديث طرق العرض)Dispatchers.IO— لعمليات الشبكة والقرصDispatchers.Default— للمهام التي تستهلك قدرًا كبيرًا من وحدة المعالجة المركزية
استخدم withContext(Dispatchers.IO) { } للتبديل بين الموزّعات داخل coroutine.
مثال على viewModelScope
اجلب البيانات في ViewModel من دون حظر واجهة المستخدم:
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.*
class DataViewModel : ViewModel() {
fun loadData() {
viewModelScope.launch {
// Switch to IO thread for network
val data = withContext(Dispatchers.IO) {
// simulate network request
delay(1000)
"Result from server"
}
// Back on Main thread — safe to update UI
_uiState.value = data
}
}
}معالجة الأخطاء في Coroutines
عالج الاستثناءات داخل coroutine باستخدام try/catch، تمامًا كما تفعل في الشيفرة العادية:
viewModelScope.launch {
try {
val result = withContext(Dispatchers.IO) {
api.fetchUser() // may throw IOException
}
_user.value = result
} catch (e: Exception) {
_error.value = "Failed: ${e.message}"
}
}Coroutines متوازية
شغّل مهمتين بالتوازي وانتظر انتهاء كلتيهما باستخدام async/await:
viewModelScope.launch {
// Both run at the same time!
val userDeferred = async(Dispatchers.IO) { api.getUser() }
val postsDeferred = async(Dispatchers.IO) { api.getPosts() }
val user = userDeferred.await()
val posts = postsDeferred.await()
_state.value = UiState(user, posts)
}تحقّق سريع
أي موزّع coroutine ينبغي استخدامه لعمليات الشبكة وقاعدة البيانات؟
مراجعة: Coroutines
يمكنك الآن كتابة شيفرة غير متزامنة من دون callbacks:
suspend fun— يمكنها التوقف مؤقتًا من دون حظر الخيطviewModelScope.launch { }— بدء coroutine في ViewModelwithContext(Dispatchers.IO)— التبديل إلى خيط يعمل في الخلفيةasync { }.await()— تنفيذ عمل بالتوازي مع الحصول على نتيجة- تعمل try/catch بصورة طبيعية داخل coroutines
التالي: نمط Repository — فصل واضح بين مصادر البيانات.
الأسئلة الشائعة
هل درس «Coroutines والدوال المعلّقة» مجاني؟
نعم — نص درس «Coroutines والدوال المعلّقة» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Android Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Android Academy 6 دروس في المجموع.
ماذا ستتعلم في «Coroutines والدوال المعلّقة»؟
اكتبوا تعليمات برمجية غير متزامنة تبدو كأنها متزامنة. استخدموا الدوال المعلّقة ونطاقات coroutines وdispatchers (IO وMain وDefault). تتمرن على Android Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Android Academy؟
لا تُشترط خبرة سابقة. Android Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 6.
كم من الوقت يستغرق درس «Coroutines والدوال المعلّقة»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Android Academy هذا؟
نعم. كل درس في Android Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- ViewModel وLiveData
- قاعدة بيانات Room
- Coroutines والدوال المعلّقة
- نمط Repository
- مكوّن Navigation
- حقن التبعيات باستخدام Hilt