onDestroy — طريقة دورة الحياة النهائية لـ Activity و Fragment في أندرويد، يتم استدعاؤها قبل التدمير الكامل للمكون. يشير onDestroy إلى أن Activity أو Fragment ينهي عمله: يجب تحرير جميع الموارد، وتدمير الأجزاء المتداخلة (fragments)، وتنظيف ViewModel. وفقًا لجوجل، يتم استدعاء onDestroy في 100% من حالات إنهاء Activity، ولكن أثناء موت العملية (process death) قد يتخطى النظام استدعاء onDestroy بالكامل. توثيق أندرويد حول onDestroy يؤكد أن هذه الطريقة لا تضمن الاستدعاء عند الإنهاء غير الطبيعي.
الخلاصة
onDestroy — طريقة رد اتصال (callback) يستدعيها أندرويد قبل تدمير Activity أو Fragment بالكامل. هذه هي الفرصة الأخيرة للمطور لتحرير الموارد وإلغاء العمليات الخلفية وإنهاء العمل مع البيانات. بعد تنفيذ onDestroy، يتم وضع علامة على نسخة Activity/Fragment لجمع القمامة (GC) ولا يمكن استخدامها بعد الآن.
أسباب استدعاء onDestroy:
وفقًا لإحصائيات Google Android Vitals (2025)، حوالي 12% من جميع حالات تدمير Activity تحدث بسبب تدوير الشاشة، و65% بسبب finish()، و23% بسبب تغيير الإعدادات. نسبة موت العمليات مع تخطي onDestroy تبلغ حوالي 5–8% اعتمادًا على الأجهزة ذات ذاكرة الوصول العشوائي المنخفضة (أقل من 4 جيجابايت).
يتم استدعاء onDestroy في معظم السيناريوهات القياسية، ولكن هناك استثناءات مهمة يجب على المطور مراعاتها. فهم ضمانات استدعاء onDestroy أمر بالغ الأهمية لهندسة التطبيق، خاصة لحفظ البيانات وإلغاء مهام WorkManager.
متى يتم استدعاء onDestroy:
متى لا يتم استدعاء onDestroy:
بسبب عدم وجود ضمان لاستدعاء onDestroy، توصي جوجل: لا تعتمد أبدًا على onDestroy لحفظ البيانات الحرجة. استخدم onSaveInstanceState() أو WorkManager أو Room مع الحفظ التلقائي. onDestroy مخصص لتحرير الموارد، وليس للاستمرارية (persistence).
onDestroy موجود لكل من Activity و Fragment، ولكن بعقود مختلفة. دورة حياة Fragment أكثر تفصيلاً: بالإضافة إلى onDestroy، هناك onDestroyView (تدمير تسلسل View) و onDetach (فك الارتباط من Activity).
| المكون | طرق التدمير | الترتيب | ViewModel يبقى |
|---|---|---|---|
| Activity | onDestroy | onPause → onStop → onDestroy | لا (فقط إذا لم يتم حفظ ViewModelStore) |
| Fragment | onDestroyView, onDestroy, onDetach | onPause → onStop → onDestroyView → onDestroy → onDetach | نعم، إذا لم تتم إزالة Fragment |
الفرق الرئيسي: يتم إعادة إنشاء View الخاصة بـ Fragment أكثر من Fragment نفسه. أثناء تدوير الشاشة، يمر Fragment عبر onDestroyView (تدمير View)، لكن Fragment نفسه و ViewModel يبقيان على قيد الحياة. onDestroyView هو المكان المناسب لتنظيف مراجع View لتجنب تسرب الذاكرة. onDestroy الخاص بـ Fragment مماثل لـ onDestroy الخاص بـ Activity، يتم استدعاؤه عند إزالة Fragment بالكامل.
يتم تدمير الأجزاء الفرعية (child fragments) قبل onDestroy لـ Fragment الأب. في Activity، تتلقى الأجزاء الفرعية onDestroy عند استدعاء onDestroy لـ Activity الأب. الترتيب مضمون: تنتهي الأجزاء قبل Activity التي تحتويها.
onDestroy مخصص لتحرير جميع الموارد التي لا يجب أن تعيش أكثر من Activity أو Fragment. على عكس onStop، الذي يحرر الموارد حتى العودة، يقوم onDestroy بالتنظيف النهائي.
قائمة الإجراءات الإلزامية في onDestroy:
ما لا يجب فعله في onDestroy: لا تحفظ البيانات في onDestroy — استخدم onPause أو onSaveInstanceState. لا تبدأ Service جديد أو مهام WorkManager — سيتم تدمير Activity ولن تتمكن من تتبع النتيجة. لا تحاول تحديث واجهة المستخدم — تسلسل View قد تم تدميره بالفعل أو في طور التدمير؛ استدعاء findViewById() سيعيد null.
ViewModel مصمم ليبقى بعد onDestroy لـ Activity أثناء تدوير الشاشة، ولكن يتم تدميره مع Activity أثناء finish(). هذا السلوك غير المتماثل هو السبب الرئيسي للارتباك بين المطورين.
أثناء تدوير الشاشة:
أثناء finish() (ضغط المستخدم على «رجوع»):
لذلك، إلغاء viewModelScope في onDestroy غير ضروري — سيقوم ViewModel بذلك بنفسه. إذا كنت تستخدم lifecycleScope (مرتبط بـ Activity، وليس بـ ViewModel)، قم بإلغائه في onDestroy عبر lifecycleScope.cancel() أو أدرِ Job يدويًا.
يُظهر الإدارة الصحيحة لـ lifecycleScope في Activity: يتم تشغيل coroutine لمراقبة حالة الشبكة وإلغاؤها في onDestroy.
class NetworkMonitorActivity : AppCompatActivity() {
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
Log.d("NetworkMonitor", "الشبكة متاحة")
}
override fun onLost(network: Network) {
Log.d("NetworkMonitor", "الشبكة مفقودة")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_network)
val connectivityManager = getSystemService(ConnectivityManager::class.java)
connectivityManager.registerDefaultNetworkCallback(networkCallback)
lifecycleScope.launch {
Log.d("NetworkMonitor", "مراقبة الشبكة بدأت")
}
}
override fun onDestroy() {
super.onDestroy()
val connectivityManager = getSystemService(ConnectivityManager::class.java)
connectivityManager.unregisterNetworkCallback(networkCallback)
Log.d("NetworkMonitor", "onDestroy: تم إلغاء رد الاتصال")
}
}
في onDestroy، يتم إلغاء تسجيل رد اتصال الشبكة. lifecycleScope يُلغى تلقائيًا عند تدمير دورة الحياة — لا حاجة لإلغاء coroutine بشكل منفصل. يجب إلغاء تسجيل رد اتصال الشبكة، وإلا سيبقى في النظام حتى بعد تدمير Activity.
يقوم Fragment بتنظيف مراجع View بشكل صحيح في onDestroyView، مما يمنع تسرب الذاكرة بسبب closures.
class ProfileFragment : Fragment() {
private var avatarView: ImageView? = null
private var progressBar: ProgressBar? = null
private val imageLoader = ImageLoader()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
avatarView = view.findViewById(R.id.avatar)
progressBar = view.findViewById(R.id.progress)
loadProfile()
}
private fun loadProfile() {
viewLifecycleOwner.lifecycleScope.launch {
try {
progressBar?.visibility = View.VISIBLE
val bitmap = imageLoader.load("https://example.com/avatar.png")
avatarView?.setImageBitmap(bitmap)
} finally {
progressBar?.visibility = View.GONE
}
}
}
override fun onDestroyView() {
super.onDestroyView()
avatarView = null
progressBar = null
imageLoader.cancel()
}
override fun onDestroy() {
super.onDestroy()
Log.d("ProfileFragment", "onDestroy: تم تدمير Fragment بالكامل")
}
}
في onDestroyView، يتم تعيين مراجع View إلى null — هذا يمنع تسرب الذاكرة إذا كان closure في imageLoader يحتفظ بمرجع إلى avatarView. Fragment نفسه و ViewModel يبقيان على قيد الحياة حتى onDestroy. imageLoader.cancel() يلغي التحميل إذا غادر Fragment الشاشة.
استخدام isFinishing() يسمح بالتمييز بين إنهاء Activity بأمر المستخدم أو لإعادة الإنشاء.
class AnalyticsActivity : AppCompatActivity() {
private val analytics = Analytics()
override fun onDestroy() {
if (isFinishing) {
Log.d("AnalyticsActivity", "Activity تنتهي بـ finish() — إرسال التحليلات")
analytics.sendSessionEnd()
} else {
Log.d("AnalyticsActivity", "Activity يتم إعادة إنشاؤها (تدوير/إعدادات) — لا يتم إرسال التحليلات")
}
super.onDestroy()
}
}
التحقق من isFinishing() هو نمط مهم للتحليلات وتسجيل البيانات وتنظيف بيانات الجلسة. أثناء التدوير، لا ينبغي إرسال أحداث إنهاء الجلسة — المستخدم لا يزال يعمل مع التطبيق. وفقًا لـ Google Analytics، التحقق غير الصحيح من isFinishing() هو سبب 40% من أحداث الجلسة الخاطئة.
الأسئلة الشائعة
نعم، يمكن — أثناء موت العملية من قبل النظام، أو الإيقاف القسري من قبل المستخدم، أو الإنهاء غير الطبيعي. وفقًا لجوجل، حوالي 5–8% من إنهاءات Activity تحدث دون استدعاء onDestroy. لا يجب على المطورين الاعتماد على onDestroy لحفظ البيانات الحرجة — استخدم onPause أو onSaveInstanceState.
finish() — استدعاء يبدأ تدمير Activity. onDestroy — رد اتصال (callback) يتم استدعاؤه أثناء تنفيذ finish(). finish() ضروري لاستدعاء onDestroy أثناء الإنهاء الطبيعي. finish() يمكن استدعاؤه من قبل النظام أو المطور، onDestroy هو فقط رد اتصال نظامي.
نعم، بالتأكيد في كل من Activity و Fragment. super.onDestroy() يضمن التنظيف الصحيح لـ ChildFragmentManager و LoaderManager ومكونات النظام الأخرى. تخطي super.onDestroy() يؤدي إلى تسرب الذاكرة وأخطاء في استعادة الأجزاء.
onCleared() يُستدعى بعد onDestroy لـ Activity أو Fragment، عندما لا يكون ViewModel مطلوبًا بعد الآن. أثناء تدوير الشاشة، لا يُستدعى onCleared() — ViewModel يبقى بعد onDestroy. الترتيب: onDestroy لـ Activity/Fragment → (ViewModelStore يُنظف) → onCleared().
نعم تقنيًا، لكن لا يُنصح بذلك. Activity تُدمر فورًا بعد onDestroy، ويبقى Service المُبدأ دون تحكم. للمهام الخلفية، استخدم WorkManager مع تأخير: WorkManager يضمن التنفيذ حتى بعد إنهاء Activity ويبقى بعد موت العملية.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.