Inactive — حالت انتقالی چرخه حیات برنامه بین Active و Background است که در آن برنامه روی صفحه قابل مشاهده است، اما رویدادهای لمسی را دریافت نمیکند. توضیح میدهیم که Inactive در iOS و Android چگونه ایجاد میشود، کدام متدهای delegate مسئول آن هستند و چگونه وقفهها — تماسها، اعلانها و حرکات سیستمی — را به درستی مدیریت کنیم.
نکات اصلی
Inactive — حالت میانی چرخه حیات برنامه موبایل است که در انتقال بین Active و Background رخ میدهد. در این حالت برنامه همچنان در پیشزمینه قرار دارد و برای کاربر قابل مشاهده است، اما رویدادهای لمسی، فشار کلیدها و سایر رویدادهای UI را دریافت نمیکند. سیستم انتقال رویدادها به برنامه را مسدود میکند، اما UI روی صفحه باقی میماند و جمع نمیشود.
ماهیت Inactive موقتی است. این حالت دقیقاً به اندازه مدت وقفه سیستمی طول میکشد: از 0.1 ثانیه هنگام بستن سریع Control Center تا چند ثانیه هنگام تماس ورودی با صفحه تماس. پس از پایان وقفه، برنامه یا به Active بازمیگردد یا اگر کاربر به برنامه دیگری سوئیچ کرده باشد به Background میرود. Inactive تنها حالتی است که انتقال در هر دو جهت امکانپذیر است: بازگشت به Active یا ادامه به Background.
در iOS، Inactive به طور خودکار توسط سیستم مدیریت میشود. توسعهدهنده نمیتواند مدت زمان ماندن در Inactive را افزایش یا کاهش دهد — این کاملاً توسط UIApplication کنترل میشود. تنها کاری که توسعهدهنده میتواند انجام دهد، مدیریت صحیح ورود به Inactive از طریق applicationWillResignActive و بازگشت از طریق applicationDidBecomeActive است. در Android معادل آن onPause است، اگرچه معناشناسی متفاوت است: onPause حتی هنگام پوشش جزئی Activity توسط مؤلفه دیگر فراخوانی میشود.
در iOS، Inactive یک حالت جداگانه از چرخه حیات برنامه است (یکی از پنج حالت: Not Running, Active, Inactive, Background, Suspended). در Android معادل مستقیمی وجود ندارد — onPause نشان میدهد که Activity فوکوس ورودی را از دست میدهد، اما ممکن است قابل مشاهده باقی بماند (مثلاً هنگام باز شدن دیالوگ). تفاوت کلیدی: Inactive در iOS حالت کلی برنامه است، onPause در Android حالت یک Activity خاص است. در حالت multi-window در Android، یک Activity ممکن است در onPause (بدون فوکوس) و دیگری در onResume (با فوکوس) باشد.
| ویژگی | iOS Inactive | Android onPause |
|---|---|---|
| UI قابل مشاهده | بله | بله (جزئی یا کامل) |
| رویدادهای لمسی | دریافت نمیکند | دریافت نمیکند |
| مدت زمان | تا پایان وقفه | تا بازگشت فوکوس یا رفتن به پسزمینه |
| حالت بعدی | Active یا Background | onResume یا onStop |
| سطح | برنامه (UIApplication) | Activity |
| Multi-window | یک صحنه فعال | چندین Activity در onPause |
Inactive در iOS در چند سناریوی کاملاً مشخص رخ میدهد. کاربر Control Center را فراخوانی میکند (کشیدن به پایین از گوشه بالا سمت راست در iPhone X+ یا کشیدن به بالا در مدلهای قدیمی). کاربر Notification Center را باز میکند (کشیدن به پایین از گوشه بالا سمت چپ). تماس ورودی دریافت میشود — سیستم صفحه تماس را روی برنامه نمایش میدهد. مجوز سیستمی درخواست میشود — موقعیت جغرافیایی، میکروفون، دوربین، مخاطبین. در iPad، Slide Over یا Split View راهاندازی میشود — صحنه فعال Inactive میشود.
در Android، onPause (معادل Inactive) در طیف گستردهتری از موقعیتها رخ میدهد. باز شدن پنجره دیالوگ (AlertDialog، DialogFragment). پوشش جزئی Activity توسط Activity دیگر (مثلاً Activity شفاف برای احراز هویت). چرخش صفحه (Activity بازسازی میشود، توالی: onPause → onStop → onDestroy → onCreate → onStart → onResume). حالت multi-window — پنجره غیرفعال onPause دریافت میکند. هر یک از این رویدادها نیاز به توقف عملیات منابعبر دارند برای صرفهجویی در باتری و عملکرد.
import UIKit
extension Notification.Name {
static let systemInterruptionBegan = Notification.Name("systemInterruptionBegan")
static let systemInterruptionEnded = Notification.Name("systemInterruptionEnded")
}
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func applicationWillResignActive(_ application: UIApplication) {
// برنامه به Inactive میرود — وقفه سیستمی
print("وقفه: Control Center، تماس یا هشدار سیستمی")
// توقف عملیات حساس به زمان
pauseVideoPlayback()
stopContinuousDataCollection()
hideSensitiveInformation()
// اطلاعرسانی به مؤلفهها
NotificationCenter.default.post(name: .systemInterruptionBegan, object: nil)
}
// بازگشت از Inactive به Active
func applicationDidBecomeActive(_ application: UIApplication) {
resumeVideoPlayback()
restartDataCollection()
NotificationCenter.default.post(name: .systemInterruptionEnded, object: nil)
}
private func pauseVideoPlayback() {
// توقف ویدیو برای جلوگیری از تداخل صدا
}
private func hideSensitiveInformation() {
// پنهانسازی دادههای حساس در اسکرینشات صفحه
// Control Center/App Switcher از UI اسکرینشات میگیرند
}
}در کد مدیریت Inactive در UIKit نشان داده شده است. applicationWillResignActive ویدیو را متوقف میکند، جمعآوری دادهها را متوقف میکند و اطلاعات حساس را پنهان میکند. این مهم است زیرا هنگام باز کردن Control Center یا App Switcher، سیستم از UI فعلی اسکرینشات میگیرد — کاربر ممکن است دادههای محرمانه را در پیشنمایش ببیند. NotificationCenter به مؤلفههای برنامه اجازه میدهد رویدادهای وقفه را مشترک شوند.
در iOS، Inactive توسط یک جفت متد مدیریت میشود: applicationWillResignActive (ورود به Inactive) و applicationDidBecomeActive (بازگشت از Inactive). این متدها بخشی از UIApplicationDelegate هستند و برای هر انتقال از طریق Inactive فراخوانی میشوند. از iOS 13 و UISceneDelegate، sceneWillResignActive و sceneDidBecomeActive برای سناریوهای multi-window به آنها اضافه شده است.
در iPad با iOS 13+، برنامه میتواند چندین صحنه (پنجره) داشته باشد. هر صحنه چرخه حیات خاص خود را دارد. یک صحنه ممکن است Inactive شود (کاربر به صحنه دیگری سوئیچ کرده است)، در حالی که دیگری Active باقی میماند. این یک تفاوت مهم با iPhone است، جایی که Inactive یک حالت سراسری برای کل برنامه است. هنگام توسعه برای iPad، Inactive باید برای هر صحنه جداگانه مدیریت شود.
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
// صحنه غیرفعال میشود
func sceneWillResignActive(_ scene: UIScene) {
// در iPad این صحنه فوکوس را از دست میدهد، اما سایرین ممکن است فعال باشند
print("صحنه فعالیت خود را از دست میدهد")
// توقف وظایف این صحنه
pauseSceneSpecificOperations()
}
// صحنه فعال میشود
func sceneDidBecomeActive(_ scene: UIScene) {
print("صحنه فعال شد")
resumeSceneSpecificOperations()
}
private func pauseSceneSpecificOperations() {
// توقف عملیات مختص این صحنه
}
private func resumeSceneSpecificOperations() {
// از سرگیری عملیات هنگام بازگشت فوکوس
}
}
// AppDelegate نقطه ورود باقی میماند، به صحنهها واگذار میکند
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions
) -> UISceneConfiguration {
return UISceneConfiguration(
name: "Default Configuration",
sessionRole: connectingSceneSession.role
)
}
}در کد، SceneDelegate برای مدیریت Inactive در سطح صحنه نشان داده شده است. sceneWillResignActive زمانی فراخوانی میشود که یک پنجره خاص فوکوس را از دست میدهد — این ممکن است هنگام جابجایی بین پنجرهها در iPad رخ دهد. AppDelegate UISceneConfiguration را برای پشتیبانی از multi-window پیکربندی میکند. هر صحنه وضعیت مستقلی دارد و توسعهدهنده باید آنها را جداگانه مدیریت کند.
در Android، معادل مستقیم Inactive iOS متد onPause() چرخه حیات Activity است. زمانی فراخوانی میشود که Activity فوکوس ورودی را از دست میدهد، اما ممکن است قابل مشاهده باقی بماند. سناریوهای معمول: باز شدن پنجره دیالوگ، راهاندازی Activity دیگر در همان برنامه، تماس ورودی، فشار دکمه Home یا Recents. در onPause، توسعهدهنده باید عملیات منابعبر — انیمیشنها، پخش ویدیو، کار با دوربین — را متوقف کند.
یک تفاوت مهم Android — onPause همیشه قبل از onStop قرار میگیرد، اما برعکس آن صادق نیست. Activity میتواند onPause را بدون onStop دریافت کند (مثلاً هنگام باز شدن Activity شفاف). همچنین onPause ممکن است چندین بار در طول عمر Activity فراخوانی شود — در هر تغییر فوکوس. منطق یکبار مصرف را در onPause قرار ندهید — برای عملیات نهایی از onStop و برای توقف اقدامات تعاملی فقط از onPause استفاده کنید.
class VideoPlayerActivity : AppCompatActivity() {
private var exoPlayer: ExoPlayer? = null
private var currentPosition: Long = 0L
override fun onPause() {
super.onPause()
// برنامه فوکوس را از دست میدهد — ویدیو را متوقف میکنیم
exoPlayer?.let { player ->
if (player.isPlaying) {
currentPosition = player.currentPosition
player.pause()
}
}
// دادههای حساس را پنهان میکنیم (GDPR/صفحات بانکی)
if (window.decorView.systemUiVisibility and
View.SYSTEM_UI_FLAG_SECURE == 0
) {
hideSensitiveOverlay()
}
}
override fun onResume() {
super.onResume()
// بازگشت فوکوس — پخش را از سر میگیریم
exoPlayer?.seekTo(currentPosition)
exoPlayer?.play()
showSensitiveOverlay()
}
private fun hideSensitiveOverlay() {
// صفحه سیاه روی دادههای مالی قرار میدهیم
}
}در کد، مدیریت صحیح onPause برای پخشکننده ویدیو نشان داده شده است. ExoPlayer متوقف میشود هنگام از دست دادن فوکوس، و موقعیت پخش ذخیره میشود. پس از بازگشت به onResume، پخشکننده پخش را از موقعیت ذخیرهشده از سر میگیرد. علاوه بر این، الگوی پنهانسازی دادههای حساس نشان داده شده است — برای برنامههای مالی و پزشکی که نیاز به محافظت در برابر اسکرینشاتها هنگام سوئیچ کردن دارند، مهم است.
قانون اول — دادههای محرمانه را هنگام انتقال به Inactive پنهان کنید. هنگامی که کاربر Control Center یا App Switcher را باز میکند، iOS از صفحه فعلی اسکرینشات میگیرد. در Android نیز به طور مشابه — سیستم پیشنمایش آخرین Activity را در Recents نشان میدهد. برای محافظت از صفحات محرمانه از UIApplication.shouldSnapshotSecureApp (iOS 16+) یا FLAG_SECURE (Android) استفاده کنید.
قانون دوم — انیمیشنها و رسانه را متوقف کنید. Inactive زمان مناسبی برای پخش ویدیو یا انیمیشن نیست، زیرا کاربر آنها را نمیبیند. علاوه بر این، پخش در پسزمینه میتواند منجر به تداخل صداها با صداهای سیستمی (تماس، اعلان) شود. AVPlayer، ExoPlayer و UIView.animate را هنگام ورود به Inactive متوقف کنید و هنگام بازگشت به Active از سر بگیرید.
قانون سوم — ورود داده را مسدود کنید. اگر برنامه حاوی فرمهای ورود یا پیشنویسهایی است، صفحه کلید و فیلدهای ورود را هنگام ورود به Inactive مسدود کنید. این کار از ورود تصادفی دادهها هنگام بازگشت جلوگیری میکند و از رهگیری دادهها از طریق پوششهای سیستمی محافظت میکند. در iOS first responder را غیرفعال کنید (view.endEditing(true))، در Android — فوکوس را پاک کنید (currentFocus?.clearFocus()).
قانون چهارم — عملیات طولانی را در applicationWillResignActive یا onPause انجام ندهید. این متدها باید در کسری از ثانیه تکمیل شوند. اگر نیاز به ذخیره حجم زیادی از داده دارید، ذخیرهسازی را در یک thread پسزمینه شروع کنید و آن را در applicationDidEnterBackground یا onStop کامل کنید. iOS 5 ثانیه برای اجرای applicationWillResignActive فرصت میدهد، پس از آن سیستم ممکن است برنامه را به اجبار خاتمه دهد.
import UIKit
final class SecureOverlayManager {
private var blurView: UIVisualEffectView?
func showBlurOverlay() {
guard let window = UIApplication.shared.keyWindow,
blurView == nil
else { return }
let blur = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
blur.frame = window.bounds
blur.autoresizingMask = [.flexibleWidth, .flexibleHeight]
window.addSubview(blur)
blurView = blur
}
func removeBlurOverlay() {
blurView?.removeFromSuperview()
blurView = nil
}
}
// استفاده در AppDelegate
func applicationWillResignActive(_ application: UIApplication) {
SecureOverlayManager().showBlurOverlay()
}
func applicationDidBecomeActive(_ application: UIApplication) {
SecureOverlayManager().removeBlurOverlay()
}در کد، پیادهسازی پوشش امن برای محافظت از دادهها هنگام انتقال به Inactive نشان داده شده است. UIVisualEffectView با افکت blur هنگام ورود به Inactive روی کل UI قرار میگیرد و هنگام بازگشت به Active حذف میشود. این تضمین میکند که دادههای محرمانه در اسکرینشاتهای App Switcher و Control Center قابل مشاهده نباشند. به طور مشابه میتوان از UIImageView با لوگو برای پوشش برند استفاده کرد.
سوالات متداول
بله. Inactive یک حالت میانی اجباری قبل از انتقال به Background در iOS است. برنامه نمیتواند مستقیماً از Active به Background برود — ابتدا Inactive میشود، سپس Background. در Android نیز به طور مشابه: onPause همیشه قبل از onStop فراخوانی میشود. این به توسعهدهنده امکان میدهد دادهها را برای ذخیرهسازی قبل از رفتن کامل به پسزمینه آماده کند.
بله. در iPad هنگام راهاندازی Slide Over یا Split View، صحنه فعال Inactive میشود، اگرچه هیچ وقفه سیستمی رخ نمیدهد — کاربر به سادگی با صحنه دیگری تعامل دارد. این ویژگی multi-window iPadOS است. در iPhone، Inactive همیشه توسط یک وقفه سیستمی — تماس، اعلان، Control Center یا Notification Center — ایجاد میشود.
معمولاً از 0.1 تا 2 ثانیه. هنگام تماس ورودی با صفحه تماس — تا 30 ثانیه (تا زمانی که کاربر پاسخ دهد یا تماس را رد کند). iOS زمان ماندن در Inactive را به اجبار محدود نمیکند، اما سیستم میتواند برنامه را خاتمه دهد اگر به رویدادها پاسخ ندهد (watchdog). در Android، onPause محدودیت زمانی ندارد، اما توصیه میشود کار در 200 میلیثانیه تکمیل شود.
ScenePhase.inactive — مقدار enum ScenePhase که وقتی صحنه در پیشزمینه است اما رویدادها را دریافت نمیکند تنظیم میشود. در SwiftUI میتوانید آن را از طریق @Environment(\.scenePhase) مشاهده کنید و از طریق onChange واکنش نشان دهید. هنگام انتقال از .active به .inactive تایمرها و انیمیشنها را متوقف کنید. هنگام بازگشت به .active — از سر بگیرید. هنگام رفتن به .background — وضعیت را ذخیره کنید.
خیر، فقط برای برنامههایی که با دادههای محرمانه کار میکنند: بانکی، پزشکی، شرکتی، پیامرسانها با چتهای خصوصی. برای بازیها و برنامههای سرگرمی پنهانسازی UI لازم نیست. با این حال، توقف بازی و صدا در Inactive یک روش خوب است تا از تداخل صداها با اعلانهای سیستمی جلوگیری شود. Apple پنهانسازی دادههای حساس را توصیه میکند اما آن را الزامی نمیداند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.