0Pricing
Android Academy · درس

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 — تُلغى عند إزالة ViewModel
  • lifecycleScope — تُلغى عند تدمير Activity أو Fragment
  • GlobalScope — تستمر إلى الأبد، لذا تجنّبها
  • 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 في ViewModel
  • withContext(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 يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. ViewModel وLiveData
  2. قاعدة بيانات Room
  3. Coroutines والدوال المعلّقة
  4. نمط Repository
  5. مكوّن Navigation
  6. حقن التبعيات باستخدام Hilt
← العودة إلى Android Academy