onPause: چیست، ذخیره‌سازی وضعیت Activity در اندروید

نویسنده: IT Sectr منتشر شده: 2026-03-04 زمان مطالعه: 10 دقیقه

onPause — روشی از چرخه حیات اندروید است که زمانی فراخوانی می‌شود که Activity فوکوس ورودی را از دست می‌دهد، اما همچنان تا حدی روی صفحه قابل مشاهده است. سیستم onPause را قبل از اینکه Activity جدید به پیش‌زمینه بیاید، هنگام باز شدن پنجره دیالوگ، پس از فشار دکمه «برنامه‌های اخیر» یا هنگام تماس ورودی فراخوانی می‌کند. این روش — آخرین نقطه تضمین‌شده برای ذخیره داده‌های کاربر است، زیرا پس از onStop و onDestroy سیستم می‌تواند بدون فراخوانی‌های اضافی فرآیند را خاتمه دهد. درون onPause توسعه‌دهنده پیش‌نویس‌ها را ذخیره می‌کند، انیمیشن‌ها را متوقف می‌کند، دوربین را آزاد می‌کند و وضعیت فعلی UI را در SharedPreferences می‌نویسد. درباره چرخه حیات کامل Activity در مقاله Activity Lifecycle بیشتر بخوانید.

نکات اصلی

  • onPause — Activity فوکوس را از دست می‌دهد اما قابل مشاهده می‌ماند؛ آخرین نقطه تضمین‌شده ذخیره داده
  • ذخیره‌سازی وضعیت — در onPause داده‌های حیاتی کاربر ذخیره می‌شود: پیش‌نویس‌ها، متن در فرم‌ها، پیشرفت
  • آزادسازی منابع — دوربین، میکروفون، پخش‌کننده ویدیو در onPause برای تحویل به برنامه دیگر آزاد می‌شوند
  • محدودیت زمان — onPause باید در 100 میلی‌ثانیه کامل شود؛ تجاوز باعث ANR و تأخیر در انتقال می‌شود
  • SharedPreferences.apply() — نوشتن ناهمزمان در onPause؛ commit() نخ را مسدود می‌کند و ممکن است ANR ایجاد کند
  • onPause vs onStop — onPause در نمایانی جزئی (دیالوگ)، onStop در پنهان‌شدن کامل (Activity دیگر)
  • onSaveInstanceState — پس از onPause برای ذخیره وضعیت موقت در Bundle فراخوانی می‌شود

onPause در اندروید چیست

onPause — چهارمین روش چرخه حیات Activity است که وقتی صفحه نمایش فوکوس ورودی را از دست می‌دهد، اما همچنان تا حدی برای کاربر قابل مشاهده است، فراخوانی می‌شود. این یک وضعیت «انتقالی» بین کار فعال برنامه و پنهان شدن آن است. سیستم onPause را در سناریوهای زیر فراخوانی می‌کند: باز شدن Activity دیگر (صفحه جدید صفحه فعلی را می‌پوشاند)، ظاهر شدن پنجره دیالوگ (Dialog، PopupWindow، Snackbar onPause را فراخوانی نمی‌کنند، اما DialogFragment فراخوانی می‌کند)، فشار دکمه «برنامه‌های اخیر»، تماس ورودی، فشار دکمه «پاور» برای قفل صفحه.

وظیفه اصلی onPause آماده‌سازی برنامه برای این است که ممکن است پنهان یا نابود شود. این آخرین نقطه در چرخه حیات است که توسعه‌دهنده می‌تواند مطمئن باشد کدش قبل از ادامه انتقال سیستم به مؤلفه دیگر اجرا می‌شود. پس از onPause سیستم onStop را فراخوانی می‌کند (اگر Activity کاملاً پنهان شود)، پس از آن نابودی فرآیند ممکن است در هر لحظه بدون اطلاع‌رسانی اضافی رخ دهد.

طبق مستندات Android Developers (2025)، onPause باید حداکثر سبک و سریع باشد. تا زمانی که onPause کنترل را برنگرداند، سیستم نمی‌تواند Activity بعدی را راه‌اندازی کند — این به این معنی است که کاربر تأخیر در انتقال بین صفحات را می‌بیند. گوگل توصیه می‌کند onPause را در کمتر از 100 میلی‌ثانیه به پایان برسانید و تمام عملیات طولانی (ذخیره در پایگاه داده، نوشتن روی دیسک) را به صورت ناهمزمان از طریق کوروتین‌ها یا apply() انجام دهید.

onPause در Activity

در Activity، روش onPause هر زمان که صفحه نمایش از فعال بودن بازمی‌ایستد، اما ممکن است همچنان تا حدی نمایش داده شود، فراخوانی می‌شود. مثال معمولی: کاربر برنامه «نقشه» را باز می‌کند، روی «اشتراک‌گذاری موقعیت» کلیک می‌کند، و بالای نقشه دیالوگ سیستمی انتخاب برنامه باز می‌شود. Activity نقشه onPause دریافت می‌کند، اما زیر دیالوگ قابل مشاهده می‌ماند. وقتی دیالوگ بسته می‌شود، نقشه بدون فراخوانی onStart onResume دریافت می‌کند (صفحه کاملاً پنهان نشده بود).

kotlin
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

onPause — آخرین نقطه‌ای است که توسعه‌دهنده می‌تواند قبل از پنهان شدن یا خاتمه برنامه توسط سیستم، داده‌های کاربر را تضمین‌شده ذخیره کند. پس از onStop سیستم می‌تواند در صورت کمبود حافظه بدون فراخوانی onDestroy فرآیند را نابود کند. روش onSaveInstanceState() پس از onPause فراخوانی می‌شود، اما Bundle آن برای ذخیره طولانی‌مدت طراحی نشده است — فقط تا onCreate بعدی زنده می‌ماند.

SharedPreferences با apply()

SharedPreferences با apply() ناهمزمان — روش بهینه برای ذخیره حجم کمی داده در onPause است. برخلاف commit() که داده‌ها را به صورت همزمان روی دیسک می‌نویسد و boolean برمی‌گرداند، apply() فوراً داده را در حافظه ذخیره می‌کند و نوشتن ناهمزمان روی دیسک را برنامه‌ریزی می‌کند. این کار در نخ UI کمتر از 1 میلی‌ثانیه در مقابل 10–100 میلی‌ثانیه commit() زمان می‌برد.

kotlin
override fun onPause() {
    super.onPause()

    // ❌ بد: نوشتن همزمان نخ را مسدود می‌کند
    // prefs.edit().putInt("score", score).commit()

    // ✅ خوب: نوشتن ناهمزمان
    prefs.edit().putInt("score", score).apply()

    // برای اشیاء پیچیده — ذخیره در ViewModel
    viewModel.saveState()
}

Room و کوروتین‌ها

برای داده‌های ساختاریافته (SQLite از طریق Room) در onPause از کوروتین‌ها با lifecycleScope استفاده می‌شود. ViewModelScope به طور خودکار کوروتین را هنگام نابودی ViewModel لغو می‌کند که از نوشتن در پایگاه داده بسته جلوگیری می‌کند. نوشتن از طریق Room با کوروتین‌ها 5–15 میلی‌ثانیه طول می‌کشد و نخ UI را مسدود نمی‌کند.

kotlin
// در 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

onPause در Fragment زمانی فراخوانی می‌شود که Fragment از فعال بودن بازمی‌ایستد، اما ممکن است قابل مشاهده بماند. این اتفاق می‌افتد وقتی: Fragment با Fragment دیگر از طریق FragmentTransaction جایگزین می‌شود؛ Fragment دیگر صفحه جاری در ViewPager نیست؛ Activity حاوی Fragment onPause دریافت می‌کند. تعامل بین onPause Activity و onPause Fragment کاملاً سلسله‌مراتبی است: ابتدا Activity onPause دریافت می‌کند، سپس همه Fragmentهای آن.

kotlin
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 vs onStop: تفاوت و سناریوها

یکی از رایج‌ترین سردرگمی‌ها در میان توسعه‌دهندگان مبتدی اندروید — عدم درک تفاوت بین onPause و onStop است. بیایید هر سناریو را بررسی کرده و روش صحیح را تعیین کنیم.

سناریوonPauseonStop
باز شدن پنجره دیالوگفراخوانی می‌شودفراخوانی نمی‌شود
باز شدن Activity جدید (غیر شفاف)فراخوانی می‌شودفراخوانی می‌شود
فشار دکمه «خانه»فراخوانی می‌شودفراخوانی می‌شود
قفل صفحهفراخوانی می‌شودفراخوانی می‌شود
تماس ورودیفراخوانی می‌شودفراخوانی می‌شود
Activity شفاف روی صفحه فعلیفراخوانی می‌شودفراخوانی نمی‌شود
Split Screen (نیم صفحه)فراخوانی می‌شودفراخوانی نمی‌شود
PiP (Picture-in-Picture)فراخوانی می‌شودفراخوانی نمی‌شود

قانون اصلی: onPause در هر از دست دادن فوکوس فراخوانی می‌شود، onStop — فقط در از دست دادن کامل دید. اگر Activity قابل مشاهده بماند (حتی تا حدی)، onStop فراخوانی نمی‌شود. این برای حالت‌های Split Screen، PiP و Activityهای شفاف حیاتی است — در اینجا onPause/onResume کار می‌کنند، اما onStart/onStop — نه.

زمان‌بندی و عملکرد onPause

onPause — بحرانی‌ترین روش از نظر زمان در چرخه حیات است، زیرا رندر Activity بعدی را مسدود می‌کند. سیستم منتظر می‌ماند تا onPause Activity فعلی کامل شود، قبل از اینکه Activity جدید را نشان دهد. اگر onPause بیش از 100 میلی‌ثانیه اجرا شود، کاربر تأخیر انتقال را متوجه می‌شود؛ اگر بیش از 5 ثانیه — سیستم ANR نشان می‌دهد.

توصیه‌های عملکرد

راهنمای عملکرد Android گوگل (2025) توصیه‌های زیر را برای onPause ارائه می‌دهد: درخواست‌های شبکه را انجام ندهید — باید لغو یا به WorkManager منتقل شوند؛ فایل‌های بزرگ روی دیسک ننویسید — از BufferedWriter در نخ پس‌زمینه استفاده کنید؛ پرس‌وجوهای SQL پیچیده انجام ندهید — عملیات Room باید از طریق کوروتین‌ها ناهمزمان باشند؛ از ایجاد اشیاء جدید خودداری کنید — جمع‌آوری زباله در onPause تأخیر را تشدید می‌کند؛ برای SharedPreferences به جای commit() از apply() استفاده کنید.

kotlin
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

توسعه‌دهندگان باتجربه نیز در 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()

super.onPause() باید فراخوانی شود، اما برخلاف onCreate، عدم وجود آن بلافاصله باعث کرش نمی‌شود. سیستم عدم وجود super در onPause را «می‌بخشد»، اما ماشین حالت داخلی به وضعیت نادرست می‌رود. فراخوانی بعدی onResume ممکن است فوکوس ورودی را بازیابی نکند و Activity «یخ‌زده» باقی بماند. همیشه super.onPause() را در اسرع وقت فراخوانی کنید.

سوالات متداول

اگر finish() در onPause فراخوانی شود چه اتفاقی می‌افتد؟

فراخوانی finish() در onPause Activity را بلافاصله پس از بازگشت از روش خاتمه می‌دهد. این یک سناریوی صحیح است اگر در از دست دادن فوکوس نیاز به بستن صفحه باشد (مثلاً صفحه احراز هویت هنگام کوچک‌سازی برنامه). با این حال finish() چرخه کامل خاتمه را شروع می‌کند: onStop → onDestroy که تأخیر به انتقال اضافه می‌کند. فقط زمانی از finish() در onPause استفاده کنید که واقعاً ضروری باشد.

تفاوت onPause با onSaveInstanceState چیست؟

onPause — برای ذخیره داده‌هایی که باید از خاتمه فرآیند جان سالم به در ببرند (پیش‌نویس‌ها در SharedPreferences/Room). onSaveInstanceState — برای ذخیره وضعیت موقت UI که فقط تا onCreate بعدی نیاز است (موقعیت اسکرول، تب انتخاب شده). Bundle onSaveInstanceState در خاتمه کامل برنامه ذخیره نمی‌شود — فقط در حافظه وجود دارد. داده‌های onPause روی دیسک ذخیره می‌شوند و راه‌اندازی مجدد را تحمل می‌کنند.

آیا می‌توان در onPause پنجره دیالوگ باز کرد؟

توصیه نمی‌شود. باز کردن دیالوگ یا پنجره بازشو در onPause منجر به WindowLeakException می‌شود اگر Activity قبلاً خاتمه یافته باشد. اگر نیاز به نمایش اعلان در از دست دادن فوکوس دارید، از NotificationManager (اعلان‌های سیستمی) استفاده کنید — این کار ایمن و قابل انتظار برای کاربر است. برای اقدامات تأخیری از AlarmManager یا WorkManager استفاده کنید.

چرا onPause مکان تضمین‌شده ذخیره است، اما onStop — نه؟

onPause تضمین‌شده قبل از اینکه Activity از فعال بودن بازایستد فراخوانی می‌شود. onStop ممکن است فراخوانی نشود اگر سیستم برای آزادسازی حافظه فرآیند را خاتمه دهد — در این صورت onDestroy نیز فراخوانی نمی‌شود. onPause تنها روش پس از onResume است که همیشه صرف نظر از دلیل از دست دادن فوکوس فراخوانی می‌شود. به همین دلیل تمام داده‌های حیاتی دقیقاً در onPause ذخیره می‌شوند.

چگونه 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 بدون دستگاه فیزیکی پشتیبانی می‌کند.

خلاصه

  • onPause — Activity فوکوس ورودی را از دست می‌دهد اما تا حدی قابل مشاهده می‌ماند؛ آخرین نقطه تضمین‌شده ذخیره داده
  • ذخیره — SharedPreferences.apply() یا Room از طریق کوروتین‌ها؛ commit() و عملیات همزمان ممنوع هستند
  • آزادسازی منابع — دوربین، فوکوس صوتی، پخش‌کننده ویدیو در onPause برای تحویل به برنامه دیگر آزاد می‌شوند
  • محدودیت 100 میلی‌ثانیه — onPause رندر Activity بعدی را مسدود می‌کند؛ تجاوز از محدودیت باعث ANR می‌شود
  • onPause vs onStop — onPause در از دست دادن فوکوس (دید حفظ شده)، onStop در پنهان‌شدن کامل
  • Fragment.onPause — فراخوانی سلسله‌مراتبی پس از Activity.onPause؛ ویژگی برای نقشه‌ها و ViewPager
  • خطاهای معمول — نوشتن همزمان، ثبت شنوندگان، نادیده گرفتن try/catch، ذخیره اضافی
  • super.onPause() — در اسرع وقت فراخوانی کنید؛ عدم وجود آن باعث کرش نمی‌شود اما ماشین حالت را خراب می‌کند

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید