onPause — روشی از چرخه حیات اندروید است که زمانی فراخوانی میشود که Activity فوکوس ورودی را از دست میدهد، اما همچنان تا حدی روی صفحه قابل مشاهده است. سیستم onPause را قبل از اینکه Activity جدید به پیشزمینه بیاید، هنگام باز شدن پنجره دیالوگ، پس از فشار دکمه «برنامههای اخیر» یا هنگام تماس ورودی فراخوانی میکند. این روش — آخرین نقطه تضمینشده برای ذخیره دادههای کاربر است، زیرا پس از onStop و onDestroy سیستم میتواند بدون فراخوانیهای اضافی فرآیند را خاتمه دهد. درون onPause توسعهدهنده پیشنویسها را ذخیره میکند، انیمیشنها را متوقف میکند، دوربین را آزاد میکند و وضعیت فعلی UI را در SharedPreferences مینویسد. درباره چرخه حیات کامل Activity در مقاله Activity Lifecycle بیشتر بخوانید.
نکات اصلی
onPause — چهارمین روش چرخه حیات Activity است که وقتی صفحه نمایش فوکوس ورودی را از دست میدهد، اما همچنان تا حدی برای کاربر قابل مشاهده است، فراخوانی میشود. این یک وضعیت «انتقالی» بین کار فعال برنامه و پنهان شدن آن است. سیستم onPause را در سناریوهای زیر فراخوانی میکند: باز شدن Activity دیگر (صفحه جدید صفحه فعلی را میپوشاند)، ظاهر شدن پنجره دیالوگ (Dialog، PopupWindow، Snackbar onPause را فراخوانی نمیکنند، اما DialogFragment فراخوانی میکند)، فشار دکمه «برنامههای اخیر»، تماس ورودی، فشار دکمه «پاور» برای قفل صفحه.
وظیفه اصلی onPause آمادهسازی برنامه برای این است که ممکن است پنهان یا نابود شود. این آخرین نقطه در چرخه حیات است که توسعهدهنده میتواند مطمئن باشد کدش قبل از ادامه انتقال سیستم به مؤلفه دیگر اجرا میشود. پس از onPause سیستم onStop را فراخوانی میکند (اگر Activity کاملاً پنهان شود)، پس از آن نابودی فرآیند ممکن است در هر لحظه بدون اطلاعرسانی اضافی رخ دهد.
طبق مستندات Android Developers (2025)، onPause باید حداکثر سبک و سریع باشد. تا زمانی که onPause کنترل را برنگرداند، سیستم نمیتواند Activity بعدی را راهاندازی کند — این به این معنی است که کاربر تأخیر در انتقال بین صفحات را میبیند. گوگل توصیه میکند onPause را در کمتر از 100 میلیثانیه به پایان برسانید و تمام عملیات طولانی (ذخیره در پایگاه داده، نوشتن روی دیسک) را به صورت ناهمزمان از طریق کوروتینها یا apply() انجام دهید.
در Activity، روش onPause هر زمان که صفحه نمایش از فعال بودن بازمیایستد، اما ممکن است همچنان تا حدی نمایش داده شود، فراخوانی میشود. مثال معمولی: کاربر برنامه «نقشه» را باز میکند، روی «اشتراکگذاری موقعیت» کلیک میکند، و بالای نقشه دیالوگ سیستمی انتخاب برنامه باز میشود. Activity نقشه onPause دریافت میکند، اما زیر دیالوگ قابل مشاهده میماند. وقتی دیالوگ بسته میشود، نقشه بدون فراخوانی onStart onResume دریافت میکند (صفحه کاملاً پنهان نشده بود).
class NoteEditorActivity : AppCompatActivity() {
private var binding: ActivityNoteEditorBinding? = null
private val prefs by lazy {
getSharedPreferences("note_drafts", Context.MODE_PRIVATE)
}
override fun onPause() {
super.onPause()
// پیشنویس یادداشت را ذخیره میکنیم — ناهمزمان
prefs.edit()
.putString("draft_title", binding?.titleInput?.text.toString())
.putString("draft_body", binding?.bodyInput?.text.toString())
.putLong("draft_timestamp", System.currentTimeMillis())
.apply()
// ویدیو را متوقف میکنیم
binding?.videoPlayer?.pause()
// منابع انحصاری را آزاد میکنیم
releaseCamera()
releaseAudioFocus()
}
override fun onResume() {
super.onResume()
// پیشنویس را بازیابی میکنیم
binding?.titleInput?.setText(prefs.getString("draft_title", ""))
binding?.bodyInput?.setText(prefs.getString("draft_body", ""))
acquireCamera()
acquireAudioFocus()
}
}
مثال NoteEditorActivity کار صحیح با onPause را نشان میدهد: ذخیره پیشنویس در SharedPreferences از طریق apply()، توقف فایل ویدیو، آزادسازی دوربین و فوکوس صوتی. هر فراخوانی سبک و سریع است و نخ UI را به اندازهای کافی برای ANR مسدود نمیکند. به ترتیب توجه کنید: super.onPause() در خط اول فراخوانی میشود — این تضمین میکند که منطق سیستمی حتی در صورت استثنا در کد کاربر اجرا شود.
onPause — آخرین نقطهای است که توسعهدهنده میتواند قبل از پنهان شدن یا خاتمه برنامه توسط سیستم، دادههای کاربر را تضمینشده ذخیره کند. پس از onStop سیستم میتواند در صورت کمبود حافظه بدون فراخوانی onDestroy فرآیند را نابود کند. روش onSaveInstanceState() پس از onPause فراخوانی میشود، اما Bundle آن برای ذخیره طولانیمدت طراحی نشده است — فقط تا onCreate بعدی زنده میماند.
SharedPreferences با apply() ناهمزمان — روش بهینه برای ذخیره حجم کمی داده در onPause است. برخلاف commit() که دادهها را به صورت همزمان روی دیسک مینویسد و boolean برمیگرداند، apply() فوراً داده را در حافظه ذخیره میکند و نوشتن ناهمزمان روی دیسک را برنامهریزی میکند. این کار در نخ UI کمتر از 1 میلیثانیه در مقابل 10–100 میلیثانیه commit() زمان میبرد.
override fun onPause() {
super.onPause()
// ❌ بد: نوشتن همزمان نخ را مسدود میکند
// prefs.edit().putInt("score", score).commit()
// ✅ خوب: نوشتن ناهمزمان
prefs.edit().putInt("score", score).apply()
// برای اشیاء پیچیده — ذخیره در ViewModel
viewModel.saveState()
}
برای دادههای ساختاریافته (SQLite از طریق Room) در onPause از کوروتینها با lifecycleScope استفاده میشود. ViewModelScope به طور خودکار کوروتین را هنگام نابودی ViewModel لغو میکند که از نوشتن در پایگاه داده بسته جلوگیری میکند. نوشتن از طریق Room با کوروتینها 5–15 میلیثانیه طول میکشد و نخ UI را مسدود نمیکند.
// در ViewModel:
fun saveDraft(title: String, body: String) {
viewModelScope.launch(Dispatchers.IO) {
noteDao.insert(NoteDraft(title = title, body = body))
}
}
// در Activity.onPause:
viewModel.saveDraft(
binding?.titleInput?.text.toString(),
binding?.bodyInput?.text.toString()
)
onPause در Fragment زمانی فراخوانی میشود که Fragment از فعال بودن بازمیایستد، اما ممکن است قابل مشاهده بماند. این اتفاق میافتد وقتی: Fragment با Fragment دیگر از طریق FragmentTransaction جایگزین میشود؛ Fragment دیگر صفحه جاری در ViewPager نیست؛ Activity حاوی Fragment onPause دریافت میکند. تعامل بین onPause Activity و onPause Fragment کاملاً سلسلهمراتبی است: ابتدا Activity onPause دریافت میکند، سپس همه Fragmentهای آن.
class MapFragment : Fragment() {
private var mapController: MapController? = null
override fun onPause() {
super.onPause()
mapController?.stopFollowMode()
binding?.mapContainer?.alpha = 0.7f
}
override fun onResume() {
super.onResume()
binding?.mapContainer?.alpha = 1.0f
if (isVisible) {
mapController?.startFollowMode()
}
}
}
ویژگی کار با نقشهها در onPause: Google Maps و Yandex Maps در حالت ردیابی فعال (follow mode) منابع GPU قابل توجهی مصرف میکنند. هنگام از دست دادن فوکوس، منطقی است که انیمیشن نقشه را غیرفعال کرده و دفعات بهروزرسانی نشانگرها را کاهش دهید و هنگام بازگشت فوکوس، عملکرد کامل را بازیابی کنید. این کار عملکرد را بهبود میبخشد و مصرف انرژی را هنگام جابجایی بین صفحات کاهش میدهد.
یکی از رایجترین سردرگمیها در میان توسعهدهندگان مبتدی اندروید — عدم درک تفاوت بین onPause و onStop است. بیایید هر سناریو را بررسی کرده و روش صحیح را تعیین کنیم.
| سناریو | onPause | onStop |
|---|---|---|
| باز شدن پنجره دیالوگ | فراخوانی میشود | فراخوانی نمیشود |
| باز شدن Activity جدید (غیر شفاف) | فراخوانی میشود | فراخوانی میشود |
| فشار دکمه «خانه» | فراخوانی میشود | فراخوانی میشود |
| قفل صفحه | فراخوانی میشود | فراخوانی میشود |
| تماس ورودی | فراخوانی میشود | فراخوانی میشود |
| Activity شفاف روی صفحه فعلی | فراخوانی میشود | فراخوانی نمیشود |
| Split Screen (نیم صفحه) | فراخوانی میشود | فراخوانی نمیشود |
| PiP (Picture-in-Picture) | فراخوانی میشود | فراخوانی نمیشود |
قانون اصلی: onPause در هر از دست دادن فوکوس فراخوانی میشود، onStop — فقط در از دست دادن کامل دید. اگر Activity قابل مشاهده بماند (حتی تا حدی)، onStop فراخوانی نمیشود. این برای حالتهای Split Screen، PiP و Activityهای شفاف حیاتی است — در اینجا onPause/onResume کار میکنند، اما onStart/onStop — نه.
onPause — بحرانیترین روش از نظر زمان در چرخه حیات است، زیرا رندر Activity بعدی را مسدود میکند. سیستم منتظر میماند تا onPause Activity فعلی کامل شود، قبل از اینکه Activity جدید را نشان دهد. اگر onPause بیش از 100 میلیثانیه اجرا شود، کاربر تأخیر انتقال را متوجه میشود؛ اگر بیش از 5 ثانیه — سیستم ANR نشان میدهد.
راهنمای عملکرد Android گوگل (2025) توصیههای زیر را برای onPause ارائه میدهد: درخواستهای شبکه را انجام ندهید — باید لغو یا به WorkManager منتقل شوند؛ فایلهای بزرگ روی دیسک ننویسید — از BufferedWriter در نخ پسزمینه استفاده کنید؛ پرسوجوهای SQL پیچیده انجام ندهید — عملیات Room باید از طریق کوروتینها ناهمزمان باشند؛ از ایجاد اشیاء جدید خودداری کنید — جمعآوری زباله در onPause تأخیر را تشدید میکند؛ برای SharedPreferences به جای commit() از apply() استفاده کنید.
override fun onPause() {
super.onPause()
// ❌ بد: درخواست HTTP UI را مسدود میکند
// val response = api.syncSave(data).execute()
// ❌ بد: نوشتن همزمان در فایل
// FileOutputStream(file).write(data)
// ✅ خوب: ذخیره ناهمزمان
lifecycleScope.launch {
withContext(Dispatchers.IO) {
api.saveData(data)
fileDao.write(data)
}
}
// ✅ خوب: نوشتن سبک در SharedPreferences
prefs.edit().putString("key", value).apply()
}
پروفایل کردن onPause از طریق Android Studio Profiler (گراف CPU) زمان دقیق اجرا را نشان میدهد. اگر onPause بیش از 100 میلیثانیه طول بکشد، Profiler روش را به رنگ زرد، و بیش از 500 میلیثانیه — به رنگ قرمز مشخص میکند. در پروژههای تجاری IT Sectr ما از تستهای Macrobenchmark استفاده میکنیم که به طور خودکار زمان انتقال بین Activityها را بررسی کرده و در مورد پسرفت عملکرد در pipeline CI هشدار میدهند.
توسعهدهندگان باتجربه نیز در onPause اشتباه میکنند. پنج مشکل معمول و راهحلهای آنها را بررسی میکنیم.
فراخوانی Room DAO با پرسوجوی همزمان (.executeAsObservable() بدون کوروتین) در onPause نخ UI را به مدت 10–50 میلیثانیه مسدود میکند. اگر در این لحظه GC یا رقابت برای نوشتن در پایگاه داده رخ دهد، تأخیر ممکن است به 200–500 میلیثانیه برسد. راهحل: از کوروتینها با Dispatchers.IO یا apply() برای SharedPreferences استفاده کنید.
onPause مکان مناسبی برای ثبت شنوندگان نیست. اگر BroadcastReceiver را در onPause ثبت کنید، زمانی که Activity دیگر قابل مشاهده نیست فعال میماند. ثبت باید فقط در onStart/onResume باشد و در onPause/onStop — فقط لغو ثبت. استثنا — APIهای Intent-driven که قبل از فراخوانی نیاز به ثبت دارند.
اگر در onPause استثنای مدیریتنشده رخ دهد، سیستم onStop و onDestroy را فراخوانی نمیکند. Activity در وضعیت نامشخصی قفل میشود و onResume در بازگشت ممکن است منابع آزاد شده را به درستی بازیابی نکند. راهحل: عملیات حیاتی را در try/catch با ثبت لاگ از طریق Log.e() قرار دهید.
نیازی به ذخیره در onPause دادههایی نیست که به راحتی قابل بازیابی هستند. به عنوان مثال، نتایج درخواستهای API در لحظه دریافت در Room یا DataStore ذخیره میشوند، نه در onPause. فقط دادههایی را ذخیره کنید که کاربر به صورت دستی وارد کرده و نمیتواند به طور خودکار بازیابی کند — متن در فیلدها، عناصر انتخاب شده، موقعیت اسکرول.
super.onPause() باید فراخوانی شود، اما برخلاف onCreate، عدم وجود آن بلافاصله باعث کرش نمیشود. سیستم عدم وجود super در onPause را «میبخشد»، اما ماشین حالت داخلی به وضعیت نادرست میرود. فراخوانی بعدی onResume ممکن است فوکوس ورودی را بازیابی نکند و Activity «یخزده» باقی بماند. همیشه super.onPause() را در اسرع وقت فراخوانی کنید.
سوالات متداول
فراخوانی finish() در onPause Activity را بلافاصله پس از بازگشت از روش خاتمه میدهد. این یک سناریوی صحیح است اگر در از دست دادن فوکوس نیاز به بستن صفحه باشد (مثلاً صفحه احراز هویت هنگام کوچکسازی برنامه). با این حال finish() چرخه کامل خاتمه را شروع میکند: onStop → onDestroy که تأخیر به انتقال اضافه میکند. فقط زمانی از finish() در onPause استفاده کنید که واقعاً ضروری باشد.
onPause — برای ذخیره دادههایی که باید از خاتمه فرآیند جان سالم به در ببرند (پیشنویسها در SharedPreferences/Room). onSaveInstanceState — برای ذخیره وضعیت موقت UI که فقط تا onCreate بعدی نیاز است (موقعیت اسکرول، تب انتخاب شده). Bundle onSaveInstanceState در خاتمه کامل برنامه ذخیره نمیشود — فقط در حافظه وجود دارد. دادههای onPause روی دیسک ذخیره میشوند و راهاندازی مجدد را تحمل میکنند.
توصیه نمیشود. باز کردن دیالوگ یا پنجره بازشو در onPause منجر به WindowLeakException میشود اگر Activity قبلاً خاتمه یافته باشد. اگر نیاز به نمایش اعلان در از دست دادن فوکوس دارید، از NotificationManager (اعلانهای سیستمی) استفاده کنید — این کار ایمن و قابل انتظار برای کاربر است. برای اقدامات تأخیری از AlarmManager یا WorkManager استفاده کنید.
onPause تضمینشده قبل از اینکه Activity از فعال بودن بازایستد فراخوانی میشود. onStop ممکن است فراخوانی نشود اگر سیستم برای آزادسازی حافظه فرآیند را خاتمه دهد — در این صورت onDestroy نیز فراخوانی نمیشود. onPause تنها روش پس از onResume است که همیشه صرف نظر از دلیل از دست دادن فوکوس فراخوانی میشود. به همین دلیل تمام دادههای حیاتی دقیقاً در onPause ذخیره میشوند.
برای آزمایش onPause از Robolectric یا FragmentScenario از AndroidX Test استفاده میشود. FragmentScenario.create() → moveToState(State.STARTED) → moveToState(State.RESUMED) → moveToState(State.STARTED) به صورت متوالی onPause را فراخوانی میکند. سپس بررسی میشود که دادهها در SharedPreferences ذخیره شدهاند یا دوربین از طریق شیء mock آزاد شده است. Robolectric 4.12+ از شبیهسازی onPause/onResume بدون دستگاه فیزیکی پشتیبانی میکند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید