Fragment Lifecycle: الأساسيات، طريقتي onCreateView و onViewCreated

المؤلف: IT Sectr نُشر: 2026-03-04 وقت القراءة: 12 دق

Fragment Lifecycle هو تسلسل محدد بدقة من طرق الاسترجاع التي يستدعيها Android طوال حياة Fragment: من الإنشاء (onAttach) إلى الإزالة الكاملة (onDetach). Fragment لديه دورة حياة أكثر تعقيداً من Activity — فهو يشمل 11 حالة و 7 استرجاعات رئيسية. تتم إدارة Fragment Lifecycle عبر FragmentManager وترتبط ارتباطاً وثيقاً بدورة حياة Activity المضيفة. وفقاً لـ Google، يتم استخدام Fragment في 74% من تطبيقات Android التي تعمل على API Level 21+، مما يجعل فهم Fragment Lifecycle أمراً إلزامياً لتطوير Android الاحترافي. توثق Android توثيق Fragment Lifecycle جميع الحالات وضمانات الاستدعاء.

أهم النقاط

  • Fragment Lifecycle يتضمن 11 استرجاعاً: onAttach، onCreate، onCreateView، onViewCreated، onStart، onResume، onPause، onStop، onDestroyView، onDestroy، onDetach.
  • يدير FragmentManager حالات Fragment ويضمن الترتيب الصحيح للاستدعاءات أثناء المعاملات.
  • onCreateView و onViewCreated هما الطريقتان الأساسيتان لإنشاء وتهيئة واجهة مستخدم Fragment.
  • يمكن لـ Fragment البقاء بعد Activity (عند تدوير الشاشة) واستعادة الحالة عبر onSaveInstanceState.
  • viewLifecycleOwner — دورة حياة منفصلة لعرض Fragment، يتم تدميرها في onDestroyView.

Fragment Lifecycle: أساسيات دورة الحياة

Fragment Lifecycle هو مجموعة من الحالات والطرق المترابطة التي يمر بها كل مثيل Fragment من الإنشاء إلى التدمير. على عكس Activity، ترتبط دورة حياة Fragment بسياقين: Fragment نفسه (يعيش من onAttach إلى onDetach) والعرض الخاص به (يعيش من onCreateView إلى onDestroyView). هذا الفصل هو ميزة رئيسية لـ Fragment، حيث تسمح له بالبقاء بعد تدمير العرض عند تدوير الشاشة دون تدمير Fragment نفسه.

التسلسل الكامل لاسترجاعات Fragment:

  • onAttach(Context) — يرتبط Fragment بـ Activity. يتم استدعاؤه أولاً. Context هو Activity المضيفة.
  • onCreate(Bundle) — تتم تهيئة Fragment. هنا يتم إنشاء ViewModel وتكوين المحولات.
  • onCreateView(LayoutInflater, ViewGroup, Bundle) — يتم إنشاء تسلسل العرض الخاص بـ Fragment. يعيد العرض الجذر.
  • onViewCreated(View, Bundle) — تم إنشاء العرض. هنا يتم تكوين عناصر واجهة المستخدم والاشتراكات في LiveData.
  • onStart() — يصبح Fragment مرئياً. تبدأ الرسوم المتحركة ويتم تسجيل أجهزة الاستشعار.
  • onResume() — Fragment نشط ويتفاعل مع المستخدم.
  • onPause() — يفقد Fragment التركيز. تتوقف الرسوم المتحركة.
  • onStop() — Fragment غير مرئي. يتم تحرير الموارد غير الحرجة.
  • onDestroyView() — يتم تدمير تسلسل العرض. يتم إلغاء مراجع العرض.
  • onDestroy() — يتم تدمير Fragment. يتم إلغاء coroutines غير الموجودة في viewModelScope.
  • onDetach() — ينفصل Fragment عن Activity. التنظيف النهائي.

وفقاً لـ Google، يمر متوسط fragment في التطبيق الحديث بالدورة الكاملة من 3 إلى 5 مرات لكل جلسة مستخدم (بسبب تدوير الشاشة والتنقل). المعالجة الصحيحة لجميع المراحل هي أساس استقرار واجهة المستخدم.

حالات Fragment: من INITIALIZED إلى DESTROYED

يدير FragmentManager Fragment عبر خمس حالات رئيسية، محددة في الفئة Fragment.State. كل حالة تتوافق مع مجموعة محددة من الاسترجاعات التي تم تنفيذها.

الحالةالمعنىالاسترجاعات المنفذة
INITIALIZEDتم إنشاء Fragment، لكن العرض غير متوفر بعدonAttach، onCreate
CREATEDتم إنشاء العرض، لكن Fragment غير مرئي+ onCreateView، onViewCreated
STARTEDFragment مرئي لكنه غير نشط+ onStart
RESUMEDFragment نشط ويتفاعل مع المستخدم+ onResume
DESTROYEDتم تدمير Fragment+ onDestroyView، onDestroy، onDetach

يقوم FragmentManager بنقل Fragment بين الحالات بناءً على إجراءات المستخدم والأحداث النظامية. عند إضافة Fragment إلى حاوية، يمر بالتتابع عبر INITIALIZED → CREATED → STARTED → RESUMED. عند الإزالة — RESUMED → STARTED → CREATED → DESTROYED.

الحالة CREATED خاصة: قد يتم تدمير العرض (بعد onDestroyView)، لكن Fragment نفسه يبقى في حالة CREATED (بعد onDestroyView، قبل onDestroy). يسمح هذا لـ FragmentManager بالاحتفاظ بـ Fragment في الذاكرة دون عرض، وهو أمر ضروري للبقاء بعد تدوير الشاشة.

الفرق بين Fragment Lifecycle و Activity Lifecycle

يرتبط Fragment Lifecycle و Activity Lifecycle ارتباطاً وثيقاً لكن لديهما اختلافات أساسية. يعيش Fragment دائماً داخل Activity، وتعتمد دورة حياته على Activity المضيفة، لكنها ليست مطابقة لها.

الجانبActivityFragment
عدد الاسترجاعات7 (onCreate … onDestroy)11 (onAttach … onDetach)
دورة حياة منفصلة للعرضلانعم (viewLifecycleOwner)
البقاء بعد التدويرلا (يتم تدميره)نعم (ViewModel + Fragment يبقيان)
الاعتماد على المضيفلايعتمد على Activity Lifecycle
حفظ الحالةonSaveInstanceStateonSaveInstanceState (على مستوى Fragment)
الإدارةالنظامFragmentManager

الفرق العملي الرئيسي: عند تدوير الشاشة، يتم تدمير Activity بالكامل (onDestroy) وإعادة إنشائها (onCreate). Fragment أثناء التدوير يمر عبر onDestroyView (يتم تدمير العرض) ← onCreateView (يتم إعادة إنشاء العرض)، لكن Fragment نفسه و ViewModel يبقيان على قيد الحياة. هذا يجعل Fragment حاوية مثالية لمنطق واجهة المستخدم الذي يجب أن يبقى بعد تغييرات التكوين.

ترتيب الاستدعاءات أثناء تدوير الشاشة: Activity.onPause ← Fragment.onPause ← Activity.onStop ← Fragment.onStop ← Activity.onDestroy ← Fragment.onDestroyView ← (تم تدمير Activity) ← Activity.onCreate ← Fragment.onAttach ← Fragment.onCreate ← Fragment.onCreateView ← Fragment.onViewCreated ← Activity.onStart ← Fragment.onStart ← Activity.onResume ← Fragment.onResume.

FragmentManager: إدارة الحالات والمعاملات

FragmentManager هو الفئة المركزية المسؤولة عن إضافة وإزالة واستبدال fragments وإدارة حالاتها. يحتفظ FragmentManager بـ BackStack ويضمن الترتيب الصحيح للاسترجاعات أثناء المعاملات. كل Activity وكل Fragment متداخل له FragmentManager الخاص به.

العمليات الرئيسية لـ FragmentManager:

  • beginTransaction() — يفتح معاملة لمجموعة من العمليات.
  • add() — يضيف Fragment إلى حاوية. يمر Fragment بدورة الحياة الكاملة حتى RESUMED.
  • replace() — يستبدل Fragment الحالي بآخر جديد. يعادل remove() + add().
  • remove() — يزيل Fragment. يمر Fragment بدورة الحياة من RESUMED إلى DESTROYED.
  • hide()/show() — يخفي/يظهر Fragment دون تدمير العرض. ينتقل Fragment إلى STARTED عند الإخفاء، ويعود إلى RESUMED عند الإظهار.
  • detach()/attach() — يفصل/يعيد ربط Fragment. يفصل (detach) يدمر العرض (onDestroyView)، ويعيد الربط (attach) ينشئه من جديد (onCreateView).
  • addToBackStack() — يضيف المعاملة إلى BackStack للتنقل للخلف.

BackStack هو مكدس معاملات FragmentManager. عند الضغط على زر الرجوع في النظام، يتم التراجع عن آخر معاملة في BackStack (popBackStack()). يتم استعادة Fragment الذي تمت إزالته عبر popBackStack. إذا كان BackStack فارغاً، يؤدي الضغط على رجوع إلى إنهاء Activity.

وفقاً لـ Google، 78% من مشاكل Fragment (التكرار، الشاشات الفارغة، IllegalStateException) مرتبطة بالاستخدام غير الصحيح لـ FragmentManager. القاعدة الرئيسية: قم بتنفيذ المعاملات عبر commit() (بشكل غير متزامن) أو commitNow() (بشكل متزامن) حسب السياق. يضمن commit() الترتيب الصحيح في حالة المعاملات المتعددة.

حفظ حالة Fragment: onSaveInstanceState

يدعم Fragment آلية حفظ الحالة الخاصة به عبر onSaveInstanceState، والتي تعمل بشكل مستقل عن Activity. يحفظ Fragment الحالة في Bundle يتم تمريره إلى onCreate و onCreateView أثناء الاستعادة.

متى يحفظ Fragment الحالة:

  • عند تدوير الشاشة — يتم تدمير العرض، ويحفظ Fragment الحالة في Bundle.
  • عند إعادة ربط Fragment بـ Activity بعد موت العملية.
  • عند استدعاء onSaveInstanceState من Activity (ينشر النظام الحفظ إلى جميع fragments التابعة).

النهج الحديث: استخدم SavedStateHandle في ViewModel لحفظ حالة Fragment. يحفظ SavedStateHandle ويستعيد البيانات تلقائياً عند تدوير الشاشة وموت العملية، دون الحاجة إلى onSaveInstanceState يدوي. يوصي Google باستخدام SavedStateHandle كالطريقة المفضلة لحفظ حالة واجهة المستخدم في Fragment.

setRetainInstance (مهمل منذ Fragment 1.3): سابقاً كان يمكن الاحتفاظ بـ Fragment عبر setRetainInstance(true) عند تدوير الشاشة. تم استبدال هذا النهج بـ ViewModel + SavedStateHandle، اللذان يعملان بشكل أكثر موثوقية ولا يتطلبان تكويناً خاصاً.

viewLifecycleOwner: دورة حياة منفصلة للعرض

viewLifecycleOwner هو Lifecycle مرتبط بعرض Fragment (من onCreateView إلى onDestroyView). هذا مفهوم مهم جداً: الاشتراكات في LiveData/Flow التي تتم عبر viewLifecycleOwner تُلغى تلقائياً عند تدمير العرض (onDestroyView)، لكنها لا تؤثر على Fragment نفسه.

الفرق بين viewLifecycleOwner و lifecycle Fragment:

  • lifecycle (Fragment) — يعيش من onAttach إلى onDetach. تبقى الاشتراكات نشطة حتى بعد تدمير العرض.
  • viewLifecycleOwner — يعيش من onCreateView إلى onDestroyView. تُلغى الاشتراكات عند تدمير العرض.

لماذا هذا مهم: إذا اشتركت في LiveData عبر lifecycle Fragment (this)، فبعد onDestroyView يبقى الاشتراك نشطاً وسيحاول LiveData تحديث عرض فارغ، مسبباً NPE. الاشتراك عبر viewLifecycleOwner يضمن عدم حدوث أي تحديثات لواجهة المستخدم بعد onDestroyView.

القاعدة: في Fragment، استخدم دائماً viewLifecycleOwner للاشتراكات في LiveData و Flow و coroutines المتعلقة بواجهة المستخدم. لـ coroutines الخاصة بـ ViewModel، استخدم viewModelScope — فهو مرتبط بـ ViewModel، وليس بـ Fragment.

أمثلة كود Fragment بلغة Kotlin

مثال 1: Fragment أساسي مع onViewCreated و viewLifecycleOwner

يوضح التهيئة الصحيحة لواجهة المستخدم والاشتراك في LiveData عبر viewLifecycleOwner.

kotlin
class UserListFragment : Fragment() {
    private val viewModel: UserListViewModel by viewModels()

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        return inflater.inflate(R.layout.fragment_user_list, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val button: Button = view.findViewById(R.id.load_button)
        button.setOnClickListener { viewModel.loadUsers() }
        viewModel.users.observe(viewLifecycleOwner) { users ->
            Log.d("UserListFragment", "تحديث القائمة: ${users.size} مستخدمين")
        }
    }

    override fun onDestroyView() {
        super.onDestroyView()
        Log.d("UserListFragment", "onDestroyView: تم تدمير العرض")
    }
}

يقوم Fragment بتضخيم التخطيط في onCreateView، وتكوين واجهة المستخدم والاشتراك في LiveData في onViewCreated. الاشتراك عبر viewLifecycleOwner هو شرط إلزامي لمنع تسرب الذاكرة. يسجل onDestroyView تدمير العرض — تأكيداً على أن Fragment يبقى بعد تدوير الشاشة.

مثال 2: Fragment مع FragmentManager والمعاملات

يوضح إضافة Fragment عبر FragmentManager في Activity، والاستبدال مع BackStack والاستعادة.

kotlin
class HostActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_host)
        if (savedInstanceState == null) {
            supportFragmentManager.beginTransaction()
                .add(R.id.fragment_container, HomeFragment())
                .addToBackStack(null)
                .commit()
        }
    }

    fun openDetail(userId: String) {
        supportFragmentManager.beginTransaction()
            .replace(R.id.fragment_container, DetailFragment.newInstance(userId))
            .addToBackStack(null)
            .commit()
    }

    override fun onBackPressed() {
        if (supportFragmentManager.backStackEntryCount > 0) {
            supportFragmentManager.popBackStack()
        } else {
            super.onBackPressed()
        }
    }
}

class DetailFragment : Fragment() {
    companion object {
        fun newInstance(userId: String): DetailFragment {
            return DetailFragment().apply {
                arguments = Bundle().apply { putString("user_id", userId) }
            }
        }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val userId = arguments?.getString("user_id")
        Log.d("DetailFragment", "جارٍ تحميل تفاصيل المستخدم: $userId")
    }
}

تستخدم Activity supportFragmentManager لإدارة fragments. تضمن معاملة add() مع BackStack أنه عند الضغط على رجوع يتم استعادة HomeFragment. يقوم openDetail() باستبدال Fragment الحالي بـ DetailFragment مع وسائط. يمنع التحقق من savedInstanceState == null تكرار fragments عند تدوير الشاشة.

مثال 3: Fragment مع LifecycleObserver و StateFlow

استخدام Flow و StateFlow في Fragment مع viewLifecycleOwner للتحديثات التفاعلية لواجهة المستخدم.

kotlin
class SearchFragment : Fragment() {
    private val viewModel: SearchViewModel by viewModels()
    private var binding: FragmentSearchBinding? = null

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentSearchBinding.inflate(inflater, container, false)
        return binding!!.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding?.searchButton?.setOnClickListener {
            viewModel.search(binding?.queryInput?.text.toString())
        }
        viewLifecycleOwner.lifecycleScope.launch {
            viewModel.searchResults.collectLatest { results ->
                Log.d("SearchFragment", "نتائج البحث: ${results.size}")
            }
        }
    }

    override fun onDestroyView() {
        super.onDestroyView()
        binding = null
    }
}

يستخدم Fragment View Binding للوصول إلى العرض. يتم إلغاء coroutine viewLifecycleOwner.lifecycleScope.launch تلقائياً عند تدمير العرض. يتم إلغاء binding في onDestroyView لمنع تسرب الذاكرة. يضمن StateFlow تحديث البيانات عند إعادة إنشاء العرض.

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

كيف يختلف onViewCreated عن onCreateView؟

onCreateView ينشئ ويعيد العرض الجذر لـ Fragment. onViewCreated يتم استدعاؤه فور إنشاء العرض، مما يضمن أن العرض مهيأ بالكامل وجاهز للتكوين (findViewById، الاشتراكات). توصي Google فقط بتضخيم التخطيط في onCreateView، والقيام بكل تكوين واجهة المستخدم في onViewCreated.

متى يتم تدمير Fragment فعلياً — في onDestroy أم onDetach؟

onDestroy — يتم تدمير Fragment ككائن (يتم مسح ViewModel وإلغاء coroutines). onDetach هو آخر استرجاع، وبعده ينفصل Fragment عن Activity. عملياً، يجب تحرير جميع الموارد في onDestroyView (العرض) و onDestroy (Fragment). onDetach مخصص لتنظيف المراجع إلى Activity.

لماذا يختفي Fragment بعد تدوير الشاشة؟

يختفي Fragment إذا لم تتم إضافته إلى FragmentManager عبر معاملة مع الحفظ في BackStack أو إذا لم تستعد Activity FragmentManager في onCreate. الحل: أضف Fragment برمجياً عبر supportFragmentManager.beginTransaction().add() في onCreate مع التحقق من savedInstanceState == null.

هل يمكن أن يوجد Fragment بدون Activity؟

لا. Fragment مرتبط دائماً بـ Activity عبر FragmentManager. حتى عند تدوير الشاشة، يتم إعادة إنشاء Activity وإعادة ربط Fragment بـ Activity الجديدة. إنشاء Fragment خارج Activity مستحيل — مُنشئ Fragment يتطلب مُنشئاً فارغاً لاستعادة النظام.

ما هي fragments المتداخلة وما الحاجة إليها؟

Fragments المتداخلة (nested fragments) هي Fragments داخل Fragment آخر. تُستخدم لبناء شاشات معقدة: ألواح علامات التبويب، لوحات ذات ألسنة، master-detail. تُدار fragments المتداخلة بواسطة FragmentManager التابع (childFragmentManager). توصي Google بعدم تجاوز مستويين من التداخل لتجنب مشاكل الأداء.

الخلاصة

  • Fragment Lifecycle يتضمن 11 استرجاعاً: onAttach ← onCreate ← onCreateView ← onViewCreated ← onStart ← onResume ← onPause ← onStop ← onDestroyView ← onDestroy ← onDetach.
  • يدير FragmentManager حالات Fragment (INITIALIZED ← CREATED ← STARTED ← RESUMED ← DESTROYED) و BackStack المعاملات.
  • يبقى Fragment بعد تدوير الشاشة — يتم تدمير العرض (onDestroyView)، لكن Fragment و ViewModel يبقيان على قيد الحياة.
  • viewLifecycleOwner هو دورة حياة منفصلة لعرض Fragment؛ إلزامي لاشتراكات LiveData و coroutines واجهة المستخدم.
  • حفظ حالة Fragment — عبر onSaveInstanceState أو SavedStateHandle في ViewModel.
  • تُنفذ معاملات Fragment عبر FragmentManager مع commit() (غير متزامن) أو commitNow() (متزامن).
  • قم دائماً بإلغاء binding ومراجع العرض في onDestroyView لمنع تسرب الذاكرة.

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

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

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

اقرأ أيضًا