onStop — طريقة من دورة حياة النشاط في Android، يتم استدعاؤها من قبل النظام عندما يتوقف النشاط عن الظهور للمستخدم. ينتقل النشاط إلى الحالة Stopped بعد أن يغطيه نشاط جديد بالكامل، أو عند تصغير التطبيق. في طريقة onStop، يجب على المطور إيقاف الرسوم المتحركة، تحرير موارد الكاميرا وأجهزة الاستشعار، وحفظ مسودات البيانات المدخلة. وفقًا لـ Android Vitals (Google, 2025)، فإن المعالجة الصحيحة لـ onStop تقلل عدد حالات ANR (التطبيق لا يستجيب) عند تصغير التطبيق بنسبة 35%. بعد onStop، يمكن للنظام استدعاء onRestart (العودة إلى الشاشة) أو onDestroy (الإنهاء الكامل). توثق Android Developers دورة حياة النشاط وتصف onStop كحد بين الحالة المرئية وغير المرئية.
الخلاصة
onStop — طريقة رد اتصال من فئة AppCompatActivity (وسابقتها Activity)، يتم استدعاؤها بواسطة نظام تشغيل Android عندما يتوقف النشاط عن الظهور بالكامل للمستخدم. في هذه اللحظة، يكون النشاط مخفيًا بواسطة نشاط آخر أو نافذة حوار أو مشغل النظام أو شاشة القفل. من منظور دورة الحياة، يأتي onStop بعد onPause ويشير إلى أن النشاط لم يعد مرئيًا على الشاشة، على الرغم من أن كائن النشاط وحالته لا يزالان في الذاكرة.
عندما ينتقل النشاط إلى الحالة Stopped (متوقف)، يحتفظ بحالته في ذاكرة الوصول العشوائي — تظل جميع الحقول والتسلسل الهرمي للعرض وViewModel قابلة للوصول. هذا يميز Stopped عن الحالة Destroyed (مدمر)، حيث يتم حذف النشاط بالكامل. يمكن لواجهة النظام إنهاء عملية التطبيق في الحالة Stopped عند نقص الذاكرة — وهذا ما يسمى موت العملية. يجب على المطور حفظ البيانات الحرجة (المسودات، موضع التمرير) في onSaveInstanceState()، الذي يتم استدعاؤه قبل onStop، لضمان الاستعادة عند موت العملية.
وفقًا لوثيقة تعريف توافق Android (CDD) للإصدار 14+، فإن العملية في الحالة Stopped لها أولوية منخفضة للقتل بواسطة OOM Killer — أقل من العمليات في مرحلة الخلفية، لكن أعلى من العمليات المخزنة مؤقتًا. وفقًا لإحصائيات Google، تحدث 68% من حالات موت العملية عندما يكون النشاط في الحالة Stopped، وليس Paused.
يتم استدعاء onStop عندما يفقد النشاط الرؤية بالكامل، بغض النظر عن السبب: بدء نشاط جديد فوق النشاط الحالي، تصغير التطبيق (الضغط على الصفحة الرئيسية)، قفل الشاشة، مكالمة واردة، أو فتح حوار النظام. في كل هذه الحالات، يتلقى النشاط أولاً onPause (فقدان جزئي للتركيز)، ثم onStop (فقدان كامل للرؤية).
السيناريوهات الرئيسية لاستدعاء onStop:
من المهم أن نفهم أن onStop لا يتم استدعاؤه عند تدوير الشاشة — في هذه الحالة، يتم تدمير النشاط (onPause ← onStop ← onDestroy) وإعادة إنشائه (onCreate ← onStart ← onResume). الاستثناء هو العلم android:configChanges="orientation" في البيان، والذي يمنع إعادة إنشاء النشاط ويستدعي بدلاً من ذلك onConfigurationChanged().
يحتل onStop مكانًا مركزيًا في تسلسل دورة حياة النشاط بين الحالة المرئية وغير المرئية. التسلسل الكامل: onCreate ← onStart ← onResume ← (حالة نشطة) ← onPause ← onStop ← onDestroy (أو onRestart ← onStart ← onResume عند العودة).
| الحالة | الطريقة | الرؤية | التفاعل | الذاكرة |
|---|---|---|---|---|
| Created | onCreate | لا | لا | مخصصة |
| Started | onStart | جزئية | لا | كاملة |
| Resumed | onResume | كاملة | نعم | كاملة |
| Paused | onPause | جزئية | لا | كاملة |
| Stopped | onStop | لا | لا | كاملة* |
| Destroyed | onDestroy | لا | لا | محررة |
*في الحالة Stopped، يتم الاحتفاظ بالنشاط في الذاكرة ولكن قد يتم قتله بواسطة النظام عند نقص الموارد. أولوية قتل عمليات Stopped هي ما قبل الأخيرة، فوق العمليات الفارغة المخزنة مؤقتًا فقط.
onStop و onSaveInstanceState: يستدعي النظام onSaveInstanceState(Bundle) قبل onStop لحفظ حالة واجهة المستخدم الديناميكية. ي override المطور هذه الطريقة لحفظ قيم حقول الإدخال وموضع RecyclerView والعناصر المحددة في Bundle. حتى إذا لم يتم تدمير النشاط (المستخدم ببساطة صغر وعاد)، يتم تمرير Bundle إلى onCreate عند تغييرات التكوين. توصي Google بحفظ حالة واجهة المستخدم العابرة فقط — وليس بيانات المستودع أو ViewModel التي تعيش خارج النشاط.
في onStop، يجب على المطور تحرير جميع الموارد التي ليست ضرورية عندما لا يكون النشاط مرئيًا. هذا يقلل من حمل البطارية ووحدة المعالجة المركزية والذاكرة، ويمنع أيضًا ANR عند العودة إلى النشاط.
ما يجب تحريره في onStop:
ما لا يجب فعله في onStop: لا تقم بتنفيذ عمليات طويلة — حفظ كميات كبيرة من البيانات في قاعدة البيانات، طلبات الشبكة، الحسابات المعقدة. يتم تنفيذ onStop على الخيط الرئيسي ويمنع العودة إلى النشاط. للعمليات الطويلة، استخدم WorkManager مع تأخير أو coroutines في viewModelScope. لا تحرر موارد ViewModel — ViewModel تبقى بعد onStop وستستخدم عند العودة.
يختلف onPause و onStop في درجة فقدان الرؤية ونطاق الإجراءات الإلزامية. يتم استدعاء onPause عند فقدان جزئي للتركيز (مثل فتح نافذة حوار أو قائمة النظام)، onStop — عند فقدان كامل للرؤية. هذا الاختلاف مهم لاختيار الموارد التي يجب تحريرها في كل مرحلة.
| الخاصية | onPause | onStop |
|---|---|---|
| مستوى الرؤية | مرئي جزئيًا | غير مرئي تمامًا |
| التركيز | مفقود | مفقود |
| وقت التنفيذ | حتى 500 مللي ثانية | حتى 5 ثوانٍ (مهلة ANR) |
| الموارد المطلوب تحريرها | الحرجة (الوسائط، الكاميرا) | جميع غير المرئية (أجهزة الاستشعار، الرسوم المتحركة، الموقع) |
| الاستعادة | onResume | onRestart ← onStart ← onResume |
| أولوية العملية | عالية (أمامية) | متوسطة (خلفية) |
القاعدة العامة: في onPause، حرر موارد النظام التي تؤثر فورًا على تجربة المستخدم لتطبيق آخر (الكاميرا، مشغل الوسائط)؛ في onStop — جميع الموارد الأخرى غير الضرورية عندما يكون النشاط مخفيًا. توصي Google بحفظ بيانات المستخدم الحرجة (مسودة البريد الإلكتروني، الإعدادات) في onPause، لأن onStop قد لا يتم استدعاؤه أثناء التبديل السريع.
عندما يعود المستخدم إلى نشاط مخفي، يستدعي النظام onRestart ← onStart ← onResume. تشير طريقة onRestart إلى أن النشاط يعود من الحالة Stopped. هذه مرحلة مهمة لاستعادة واجهة المستخدم والموارد التي تم تحريرها في onStop.
تسلسل الاستدعاءات عند العودة:
إذا تم قتل عملية التطبيق بواسطة النظام في الحالة Stopped، يتم استدعاء onCreate بدلاً من onRestart، ويتم تمرير Bundle من onSaveInstanceState لاستعادة الحالة. هذا السيناريو (موت العملية) هو أحد أكثر أسباب الأخطاء شيوعًا في تطبيقات Android: يطبق المطورون onRestart لكنهم ينسون مراعاة الاستعادة عبر onCreate بعد موت العملية.
يوضح إلغاء الاشتراك الصحيح من أجهزة الاستشعار وإيقاف الرسوم المتحركة عند إخفاء النشاط. عند العودة إلى الشاشة، يتم استعادة الموارد في onStart.
class MainActivity : AppCompatActivity() {
private lateinit var sensorManager: SensorManager
private var accelerometer: Sensor? = null
private var rotationAnimator: ObjectAnimator? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
}
override fun onStart() {
super.onStart()
accelerometer?.let {
sensorManager.registerListener(sensorListener, it, SensorManager.SENSOR_DELAY_NORMAL)
}
rotationAnimator = ObjectAnimator.ofFloat(findViewById(R.id.icon), "rotation", 0f, 360f)
rotationAnimator?.apply {
duration = 3000
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
start()
}
}
override fun onStop() {
super.onStop()
sensorManager.unregisterListener(sensorListener)
rotationAnimator?.cancel()
}
override fun onRestart() {
super.onRestart()
Log.d("MainActivity", "يعود النشاط من الحالة Stopped")
}
private val sensorListener = SensorEventListener { event, _ ->
Log.d("MainActivity", "التسارع: x=${event.values[0]}، y=${event.values[1]}، z=${event.values[2]}")
}
}
يسجل الكود مستشعر مقياس التسارع ويبدأ رسمًا متحركًا دورانيًا لا نهائيًا في onStart. في onStop، يتم إلغاء تسجيل المستشعر وإلغاء الرسم المتحرك — وهذا يمنع استنزاف البطارية عندما يكون النشاط مخفيًا. بعد العودة عبر onRestart ← onStart، يتم إعادة إنشاء الموارد.
نهج حديث باستخدام ViewModel + SavedStateHandle. يتم حفظ بيانات النموذج تلقائيًا أثناء onStop دون معالجة يدوية لـ Bundle.
class FormViewModel(
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
var email: String
get() = savedStateHandle["email"] ?: ""
set(value) { savedStateHandle["email"] = value }
var message: String
get() = savedStateHandle["message"] ?: ""
set(value) { savedStateHandle["message"] = value }
}
class FormActivity : AppCompatActivity() {
private val viewModel: FormViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_form)
Log.d("FormActivity", "onCreate: email=${viewModel.email}")
}
override fun onStop() {
super.onStop()
Log.d("FormActivity", "onStop: تم حفظ البيانات في SavedStateHandle")
}
}
يقوم SavedStateHandle تلقائيًا بحفظ القيم في Bundle أثناء onSaveInstanceState، الذي يتم استدعاؤه قبل onStop. عند تدوير الشاشة أو موت العملية، يتم استعادة البيانات دون فقدان. توصي Google باستخدام SavedStateHandle للنماذج والمسودات بدلاً من onSaveInstanceState المباشر.
استخدام lifecycleScope مع coroutines لحفظ البيانات بشكل غير متزامن أثناء الانتقال إلى onStop. يتم تشغيل coroutine على مرسل IO دون حظر الخيط الرئيسي.
class NoteActivity : AppCompatActivity() {
private val noteRepository = NoteRepository()
override fun onStop() {
lifecycleScope.launch(Dispatchers.IO) {
val text = findViewById<EditText>(R.id.note_content).text.toString()
noteRepository.saveDraft(text)
withContext(Dispatchers.Main) {
Log.d("NoteActivity", "تم حفظ المسودة في onStop")
}
}
super.onStop()
}
}
يتم إلغاء coroutine lifecycleScope.launch تلقائيًا إذا انتهت دورة حياة النشاط. يضمن استخدام Dispatchers.IO أن الكتابة إلى قاعدة البيانات أو الملف لا تمنع العودة إلى النشاط. وفقًا لـ Google، فإن coroutines في lifecycleScope هي الطريقة المفضلة لتنفيذ العمليات غير المتزامنة في onStop.
الأسئلة الشائعة
onStop — يتوقف النشاط عن الظهور لكنه يبقى في الذاكرة في الحالة Stopped. يمكن للنظام إعادة النشاط عبر onRestart. onDestroy — يتم تدمير النشاط وتحرير الذاكرة. بعد onDestroy، العودة ممكنة فقط عن طريق إنشاء مثيل جديد للنشاط (onCreate).
نعم، إلزامي. يضمن super.onStop() التشغيل الصحيح لمكونات النظام: الأجزاء (fragments) و LoaderManager و ViewModelStore. تخطي super.onStop() قد يسبب تسربًا للذاكرة واستعادة غير صحيحة للأجزاء. استدعِ super.onStop() دائمًا في النهاية أو البداية — الترتيب ليس حرجًا، لكن الاستدعاء إلزامي.
استخدم Log.d أو Timber في كل طريقة من دورة الحياة. قم بتشغيل فلتر logcat بواسطة علامة النشاط الخاص بك. للإنتاج، استخدم Android Vitals — تقوم Google تلقائيًا بجمع مقاييس دورة الحياة وعرض الحالات الشاذة في Play Console. كما يتوفر مراقبة دورة الحياة عبر ProcessLifecycleOwner.
الاستثناء غير الملتقط في onStop يسبب Force Close للتطبيق. النظام لا يلتقط الاستثناءات في استدعاءات دورة الحياة. إذا كانت onStop تنفذ عمليات قد تطرح استثناءً (عمليات الملفات، الشبكة)، قم بتغليفها في try-catch وسجل الخطأ دون مقاطعة super.onStop().
لا، سيتم جمع Bitmap في النشاط بواسطة GC إذا لم تكن هناك مراجع له. التحرير القسري (recycle()) في onStop ليس ضروريًا بل قد يكون ضارًا — إذا عاد النشاط عبر onRestart، يجب تحميل Bitmap مرة أخرى. استخدم Glide أو Coil لتحميل الصور — هذه المكتبات تدير تلقائيًا التخزين المؤقت ودورة الحياة.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.