lifecycleScope — ما هو، الربط مع Lifecycle والعمل في Android

المؤلف: IT Sectr نُشر: 2026-06-23 وقت القراءة: 9 دق

lifecycleScope — هو CoroutineScope مضمن من مكتبة androidx.lifecycle، مرتبط بدورة حياة Activity أو Fragment أو أي LifecycleOwner، ويلغي الكوروتينات تلقائياً عند تدمير المكون. وفقاً لـ Google Android Developers, 2025، يسمح lifecycleScope بتشغيل الكوروتينات بأمان في طبقة واجهة المستخدم دون خطر تنفيذ برمجية بعد تدمير Activity أو Fragment. يتم إلغاء السكوب تلقائياً عند انتقال LifecycleOwner إلى الحالة DESTROYED.

النقاط الرئيسية

  • lifecycleScope — CoroutineScope من lifecycle-runtime-ktx، يلغى عند حالة DESTROYED لـ Lifecycle
  • lifecycleScope.launch — تشغيل كوروتين يلغى تلقائياً عند تدمير LifecycleOwner
  • launchWhenStarted / launchWhenResumed — طرق قديمة، تم استبدالها بـ repeatOnLifecycle
  • repeatOnLifecycle — API حديث لتشغيل الكوروتينات التي تتبع حالة محددة لـ Lifecycle
  • Dispatchers.Main.immediate — الموزع الافتراضي لـ lifecycleScope

ما هو lifecycleScope في Android؟

lifecycleScope هو خاصية تمديد على واجهة LifecycleOwner (Activity، Fragment، Service) توفر CoroutineScope جاهزاً مرتبطاً بدورة حياة المكون بأكملها. عند وصول LifecycleOwner إلى الحالة DESTROYED، يلغي lifecycleScope جميع الكوروتينات النشطة تلقائياً.

kotlin
// In Fragment or Activity
lifecycleScope.launch {
    delay(1000)
    showSnackbar("مرحباً!")
}

بالخلاف لـ viewModelScope، يتم إلغاء lifecycleScope كل مرة يتم فيها تدمير LifecycleOwner — بما في ذلك تدوير الشاشة. وهذا يجعله مثالياً للعمليات التي يجب أن تعيش فقط طالما كانت شاشة معينة مرئية.

أين يتوفر lifecycleScope

lifecycleScope متوفر في كل مكان يوجد فيه LifecycleOwner:

  • Activity — AppCompatActivity ترث LifecycleOwner
  • Fragment — Fragment يرث LifecycleOwner
  • LifecycleService — خدمة بدورة حياة
  • ProcessLifecycleOwner — دورة حياة التطبيق بأكمله
  • Custom LifecycleOwner — أي كائن ينفذ LifecycleOwner

كيف يعمل lifecycleScope: Lifecycle والإلغاء التلقائي

تعتمد آلية الإلغاء التلقائي لـ lifecycleScope على الاشتراك في أحداث Lifecycle. عندما ينخفض Lifecycle أسفل من CREATED إلى DESTROYED، يتم إلغاء السكوب.

حالات Lifecycle

الحالةالوصفالسكوب نشط
CREATEDتم إنشاء LifecycleOwner، تم تنفيذ onCreateنعم
STARTEDLifecycleOwner مرئي (onStart)نعم
RESUMEDLifecycleOwner في المقدمة (onResume)نعم
DESTROYEDتم تدمير LifecycleOwner (onDestroy)لا (تم إلغاء السكوب)

البنية الداخلية لـ lifecycleScope

يتم إنشاء lifecycleScope كـ CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) ويتم تخزينه داخل Lifecycle. عند انتقال Lifecycle إلى الحالة DESTROYED، يتم استدعاء scope.cancel(). تم تنفيذ الآلية من خلال LifecycleEventObserver، الذي يشترك في أحداث دورة الحياة عند أول وصول إلى السكوب.

السلوك عند التدوير

عند تدوير الشاشة، تتم تدمير Activity (onDestroy) وإنشاؤها من جديد. يتم إلغاء lifecycleScope مع Activity القديمة، ويتم إنشاء مثال جديد من السكوب لـ Activity الجديدة. هذا هو الفرق الأساسي عن viewModelScope، الذي يبقى عند التدوير.

API lifecycleScope: launch، launchWhen و repeatOnLifecycle

توفر مكتبة lifecycle عدة طرق لتشغيل الكوروتينات من خلال lifecycleScope. دعنا نستعرض تطور API من الطرق القديمة إلى الحديثة.

lifecycleScope.launch — التشغيل الأساسي

أبسط طريقة هي lifecycleScope.launch { ... }. تبدأ الكوروتين فوراً وتلغى عند DESTROYED. ولكنها يمكنها تنفيذ برمجية حتى عندما لا تكون واجهة المستخدم مرئية (على سبيل المثال، في الخلفية بعد onStop). وهذا ليس مرغوباً دائماً.

الطرق القديمة: launchWhenCreated / launchWhenStarted / launchWhenResumed

كانت هذه الطرق توقف تنفيذ الكوروتين عندما ينخفض Lifecycle أسفل من الحالة المحددة وتستئنفها عند العودة. ولكنها وسمت بأنها @Deprecated في lifecycle-runtime-ktx 2.6.0 لأنها:

  • لم تلغٍ الكوروتين — فقط أوقفته
  • أدت إلى تراكم الكوروتينات المعلقة التي تستهلك الذاكرة
  • خلقت سباق المستقبل عند التبديل السريع بين الحالات

API الحديث: repeatOnLifecycle

repeatOnLifecycle هو الطريقة الموصى بها من Google لتشغيل الكوروتينات المتزامنة مع دورة الحياة. يلغي ويعيد تشغيل الكوروتين كل مرة يصل فيها Lifecycle إلى الحالة المحددة.

kotlin
lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.uiState.collect { state ->
            updateUI(state)
        }
    }
}

تبدأ الكوروتين الممررة إلى repeatOnLifecycle عندما يصل Lifecycle إلى STARTED وتلغى عندما ينخفض أسفل من STARTED. عند العودة إلى STARTED، تعاد تشغيل الكوروتين من جديد. هذا آمن وفعال — لا تبقى أي كوروتينات معلقة.

flowWithLifecycle — لـ Flow

لجمع البيانات من Flow مع مراعاة دورة الحياة، هناك عامل flowWithLifecycle. يوقف ويستئنف الجمع تلقائياً عند تغير حالة Lifecycle:

kotlin
viewModel.uiState
    .flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
    .onEach { state -> updateUI(state) }
    .launchIn(lifecycleScope)

عامل flowWithLifecycle هو أكثر طريقة موجزة للاشتراك بأمان في Flow في طبقة واجهة المستخدم.

أمثلة استخدام lifecycleScope

دعنا نستعرض ثلاث سيناريوهات واقعية لاستخدام lifecycleScope في تطبيق Android باستخدام Kotlin.

مثال 1: الاشتراك في تحديثات الموقع فقط عندما تكون الشاشة مرئية

kotlin
class MapFragment : Fragment() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                locationProvider.observeLocation().collect { loc ->
                    updateMapMarker(loc)
                }
            }
        }
    }
}

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

مثال 2: تشغيل رسوم متحركة عند بدء الفراغمنت

kotlin
lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.RESUMED) {
        animateFadeIn(titleView)
        delay(200)
        animateSlideUp(contentView)
    }
}

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

مثال 3: مزامنة دورية للبيانات على شاشة مرئية

kotlin
lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        while (isActive) {
            syncData()
            delay(30_000L)
        }
    }
}

يتم مزامنة البيانات كل 30 ثانية، ولكن فقط عندما تكون الشاشة مرئية. isActive تتحقق مما إذا كانت الكوروتين قد ألغيت، مما يوفر خروجاً آمناً من الحلقة عند مغادرة الشاشة.

lifecycleScope مقابل viewModelScope: سيناريوهات الاستخدام

كلا السكوبين مرتبطان بدورة الحياة، ولكن بجوانب مختلفة منها. فهم الفرق أمر حاسم لبناء معمارية صحيحة لتطبيقات Android.

الفرق الرئيسي

viewModelScope مرتبط بـ ViewModel، الذي يبقى عند تدوير الشاشة. lifecycleScope مرتبط بـ LifecycleOwner (Activity/Fragment)، الذي يتم تدميره وإنشاؤه من جديد عند التدوير. وهذا يحدد سيناريوهات استخدامهما.

متى استخدام lifecycleScope

  • الاشتراك في أحداث النظام (الموقع، الحساسات، الكاميرا)
  • الرسوم المتحركة وتأثيرات واجهة المستخدم المرتبطة بشاشة محددة
  • تحميل البيانات بالتجزئة بناءً على رؤية الشاشة
  • العمليات التي يجب أن تتوقف عند مغادرة الشاشة

متى استخدام viewModelScope

  • تحميل البيانات من Repository
  • منطق الأعمال الذي يجب أن يبقى عند التدوير
  • التخزين المؤقت ومعالجة البيانات
  • أي عملية يلزم نتيجتها بعد التدوير

الاستخدام المشترك

في الممارسة، يشيع مزيج من كلا السكوبين: viewModelScope يحمل البيانات ويدير الحالة، بينما lifecycleScope يشترك في Flow من ViewModel مع مراعاة دورة الحياة. يعتبر هذا الفصل بين المسؤوليات أفضل ممارسة في تطوير Android الحديث.

الأخطاء الشائعة عند العمل مع lifecycleScope

دعنا نستعرض أربعة من أكثر الأخطاء شيوعاً التي يرتكبها المطورون عند استخدام lifecycleScope.

الخطأ 1: استخدام lifecycleScope بدلاً من viewModelScope لتحميل البيانات

إذا قمت بتشغيل تحميل البيانات في lifecycleScope.launch، ستلغى الكوروتين عند تدوير الشاشة، وسيتعين تحميل البيانات من جديد. استخدم viewModelScope للعمليات طويلة العمر. lifecycleScope مخصص فقط للمهام المرتبطة بواجهة المستخدم.

الخطأ 2: جمع Flow بدون repeatOnLifecycle

الاستدعاء المباشر viewModel.someFlow.collect { ... } داخل lifecycleScope.launch يستمر في جمع البيانات حتى عندما لا تكون الشاشة مرئية. يمكن أن يؤدي ذلك إلى تحديثات واجهة المستخدم في الخلفية وعبء غير ضروري. استخدم دائماً repeatOnLifecycle أو flowWithLifecycle.

الخطأ 3: نسيان الإلغاء عند مغادرة الشاشة

على الرغم من أن lifecycleScope يلغى عند DESTROYED، قد لا يتم تنفيذ البرمجية بعد نقطة التعليق عند الإلغاء المفاجئ. لا تعتمد على تنفيذ البرمجية بعد استدعاء suspend إلا إذا كنت تستخدم NonCancellable.

الخطأ 4: استخدام launchWhenStarted القديم بدلاً من repeatOnLifecycle

launchWhenStarted ونظائره لا يلغون الكوروتين، بل يوقفونها فقط. إذا تبدلت الشاشة بين المقدمة والخلفية عدة مرات، تقوم الكوروتين بتراكم الاستدعاءات المؤجلة. انتقل إلى repeatOnLifecycle — هذا هو الطريقة الصحيحة الوحيدة للتزامن مع Lifecycle.

الأسئلة الشائعة

ما الفرق بين lifecycleScope و GlobalScope؟

lifecycleScope يلغى تلقائياً عند تدمير LifecycleOwner. GlobalScope يعيش لطول مدة عمل التطبيق. لا يمكن للكوروتين في lifecycleScope تحديث واجهة المستخدم بعد تدمير المكون، بينما في GlobalScope يمكنه ذلك، مما يؤدي إلى أعطال. استخدم دائماً lifecycleScope في طبقة واجهة المستخدم.

هل يمكن استخدام lifecycleScope في ViewModel؟

لا، ViewModel ليس LifecycleOwner، لذا lifecycleScope غير متوفر فيه. ViewModel يستخدم viewModelScope. إذا كان البرمجية يجب أن تعمل في كلا السياقين، استخرج المنطق في use case أو repository مع دوال suspend.

ماذا يحدث عند استدعاء repeatOnLifecycle عدة مرات؟

كل استدعاء لـ repeatOnLifecycle ينشئ كوروتين جديد يقوم بتشغيل الكتلة عند الوصول إلى حالة Lifecycle المحددة. إذا تم استدعاء repeatOnLifecycle مرتين لنفس الحالة، ستتم تشغيل كلتا الكتلتين بشكل مستقل. عادةً، يكفي استدعاء واحد في onViewCreated.

هل يمكن تعيين Dispatcher مخصص لـ lifecycleScope؟

لا يمكنك تغيير موزع lifecycleScope مباشرة — يستخدم Dispatchers.Main.immediate. داخل كتلة الكوروتين، يمكنك التبديل إلى موزع آخر من خلال withContext. للاختبارات، استخدم TestDispatcher مع LifecycleOwner.

هل lifecycleScope يلغى في onPause أم onDestroy؟

يتم إلغاء lifecycleScope عند انتقال LifecycleOwner إلى الحالة DESTROYED (بعد onDestroy). لا يتم إلغاء استدعاءات lifecycleScope.launch البسيطة في onPause أو onStop. للإيقاف عند الذهاب إلى الخلفية، استخدم repeatOnLifecycle(STARTED) أو repeatOnLifecycle(RESUMED).

الملخص

  • lifecycleScope — CoroutineScope مرتبط بـ LifecycleOwner ويلغى تلقائياً عند DESTROYED من خلال LifecycleEventObserver
  • Dispatchers.Main.immediate — الموزع الافتراضي، يضمن تحديثات آمنة لواجهة المستخدم دون تبديل غير ضروري
  • repeatOnLifecycle — API حديث لتشغيل الكوروتينات عند حالة Lifecycle محددة مع إلغاء وإعادة تشغيل تلقائيين
  • flowWithLifecycle — عامل لجمع Flow آمن من واجهة المستخدم مع مراعاة دورة الحياة
  • launchWhenStarted قديم — بدلاً من الإيقاف، استخدم repeatOnLifecycle الذي يلغي الكوروتين بدلاً من إيقافها
  • lifecycleScope vs viewModelScope — lifecycleScope لعمليات واجهة المستخدم (رسوم متحركة، الموقع)، viewModelScope للبيانات ومنطق الأعمال
  • تدوير الشاشة — lifecycleScope يلغى عند التدوير، viewModelScope يبقى، اختر السكوب حسب المهمة

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا