Inactive — الحالة الانتقالية بين Active وBackground

المؤلف: IT Sectr نُشر: 2026-03-03 وقت القراءة: 10 دق

Inactive — حالة انتقالية في دورة حياة التطبيق بين Active وBackground، حيث يكون التطبيق مرئياً على الشاشة لكنه لا يتلقى أحداث اللمس. نشرح كيف ينشأ Inactive على iOS وAndroid، وما طرق المفوض المسؤولة عنه، وكيفية معالجة المقاطعات بشكل صحيح — المكالمات والإشعارات والإيماءات النظامية.

الملامح الرئيسية

  • Inactive — التطبيق مرئي على الشاشة لكنه لا يستقبل أحداث اللمس بسبب مقاطعة نظامية
  • applicationWillResignActive — الطريقة الرئيسية التي تشير إلى الانتقال إلى Inactive على iOS
  • طبيعة مؤقتة — يستمر Inactive من أجزاء من الثانية إلى عدة ثوانٍ، ثم ينتقل إلى Active أو Background
  • الأسباب — مكالمة واردة، Control Center، Notification Center، تنبيه نظام، Apple Pay، Siri
  • SwiftUI — scenePhase .inactive يعادل Inactive في UIKit

Inactive — جوهر الحالة الانتقالية

Inactive هو حالة وسيطة في دورة حياة التطبيق المحمول تحدث أثناء الانتقال بين Active وBackground. في هذه الحالة، لا يزال التطبيق في المقدمة ومرئياً للمستخدم، لكنه لا يتلقى أحداث اللمس أو ضغطات المفاتيح أو أحداث واجهة المستخدم الأخرى. يمنع النظام توصيل الأحداث إلى التطبيق، لكن تبقى واجهة المستخدم على الشاشة ولا يتم تصغيرها.

طبيعة Inactive مؤقتة. تستمر هذه الحالة تماماً لمدة المقاطعة النظامية: من 0.1 ثانية عند إغلاق Control Center بسرعة إلى عدة ثوانٍ أثناء مكالمة واردة مع شاشة الاتصال. بعد انتهاء المقاطعة، يعود التطبيق إلى Active أو ينتقل إلى Background إذا كان المستخدم قد انتقل إلى تطبيق آخر. Inactive هي الحالة الوحيدة التي يمكنها الانتقال في كلا الاتجاهين: العودة إلى Active أو المتابعة إلى Background.

على iOS، تتم إدارة Inactive تلقائياً بواسطة النظام. لا يمكن للمطور تمديد أو تقصير الوقت في Inactive — فهو بالكامل تحت سيطرة UIApplication. الشيء الوحيد الذي يمكن للمطور فعله هو معالجة الانتقال إلى Inactive بشكل صحيح عبر applicationWillResignActive والعودة عبر applicationDidBecomeActive. على Android، المكافئ هو onPause، على الرغم من اختلاف الدلالات: يتم استدعاء onPause حتى عندما تكون Activity مغطاة جزئياً بمكون آخر.

مقارنة Inactive على iOS وAndroid

على iOS، Inactive هي حالة منفصلة في دورة حياة التطبيق (واحدة من خمس: Not Running، Active، Inactive، Background، Suspended). على Android، لا يوجد مكافئ مباشر — يشير onPause إلى أن Activity تفقد تركيز الإدخال لكنها قد تبقى مرئية (على سبيل المثال، عند فتح حوار). الفرق الرئيسي: iOS Inactive هي حالة على مستوى التطبيق بأكمله، بينما onPause على Android هي حالة لكل Activity. في النوافذ المتعددة على Android، يمكن أن تكون إحدى Activity في onPause (بدون تركيز) بينما تكون أخرى في onResume (مع تركيز).

الخاصيةiOS InactiveAndroid onPause
واجهة المستخدم مرئيةنعمنعم (جزئياً أو كلياً)
أحداث اللمسلا يستقبللا يستقبل
المدةحتى انتهاء المقاطعةحتى عودة التركيز أو الذهاب إلى الخلفية
الحالة التاليةActive أو BackgroundonResume أو onStop
المستوىالتطبيق (UIApplication)Activity
النوافذ المتعددةمشهد واحد نشطعدة Activity في onPause

متى ينشأ Inactive

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). وضع النوافذ المتعددة — النافذة غير النشطة تستقبل onPause. كل من هذه الأحداث يتطلب تعليق العمليات كثيفة الاستخدام للموارد للحفاظ على البطارية والأداء.

swift
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 يلتقطان لقطة لواجهة المستخدم
    }
}

يظهر الكود معالجة Inactive في UIKit. applicationWillResignActive يوقف الفيديو، ويوقف جمع البيانات، ويخفي المعلومات الحساسة. هذا مهم لأنه عند فتح Control Center أو App Switcher، يلتقط النظام لقطة شاشة لواجهة المستخدم الحالية — قد يرى المستخدم بيانات سرية في المعاينة. يسمح NotificationCenter لمكونات التطبيق بالاشتراك في أحداث المقاطعة.

Inactive على iOS: Swift وUIKit

على iOS، تتم معالجة Inactive بواسطة زوج من الطرق: applicationWillResignActive (الانتقال إلى Inactive) و applicationDidBecomeActive (العودة من Inactive). هذه الطرق هي جزء من UIApplicationDelegate ويتم استدعاؤها لكل انتقال عبر Inactive. منذ iOS 13 وUISceneDelegate، تمت إضافة sceneWillResignActive وsceneDidBecomeActive لسيناريوهات النوافذ المتعددة.

SceneDelegate وInactive متعدد النوافذ

على iPad مع iOS 13+، يمكن أن يحتوي التطبيق على عدة مشاهد (نوافذ). لكل مشهد دورة حياة خاصة به. يمكن أن يصبح مشهد واحد Inactive (قام المستخدم بالتبديل إلى مشهد آخر) بينما يظل آخر Active. هذا فرق مهم عن iPhone، حيث Inactive هي حالة عامة للتطبيق بأكمله. عند التطوير لـ iPad، تحتاج إلى معالجة Inactive لكل مشهد على حدة.

swift
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 لدعم النوافذ المتعددة. لكل مشهد حالة مستقلة ويجب على المطور معالجتها بشكل منفصل.

Inactive على Android: onPause والرؤية

على Android، المكافئ المباشر لـ Inactive على iOS هو طريقة onPause() من دورة حياة Activity. يتم استدعاؤها عندما تفقد Activity تركيز الإدخال لكنها قد تبقى مرئية. السيناريوهات النموذجية: فتح حوار، تشغيل Activity أخرى في نفس التطبيق، مكالمة واردة، الضغط على زر Home أو Recents. في onPause، يجب على المطور تعليق العمليات كثيفة الاستخدام للموارد — الرسوم المتحركة، تشغيل الفيديو، العمل مع الكاميرا.

فرق مهم في Android هو أن onPause يسبق onStop دائماً، ولكن ليس العكس. يمكن أن تستقبل Activity onPause بدون onStop (على سبيل المثال، عند فتح Activity شفافة). أيضاً، يمكن استدعاء onPause عدة مرات خلال عمر Activity — عند كل تبديل تركيز. لا تضع منطقاً لمرة واحدة في onPause — استخدم onStop للعمليات النهائية وonPause فقط لتعليق الإجراءات التفاعلية.

kotlin
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

القاعدة الأولى — أخفِ البيانات السرية عند الانتقال إلى 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، قم بإنهاء دور المستجيب الأول (view.endEditing(true))، على Android — امسح التركيز (currentFocus?.clearFocus()).

القاعدة الرابعة — لا تقم بعمليات طويلة في applicationWillResignActive أو onPause. يجب أن تكتمل هذه الطرق في أجزاء من الثانية. إذا كنت بحاجة إلى حفظ كمية كبيرة من البيانات، ابدأ الحفظ في خيط خلفي واكتمله في applicationDidEnterBackground أو onStop. يعطي iOS 5 ثوانٍ لتنفيذ applicationWillResignActive، وبعدها قد يقوم النظام بإنهاء التطبيق قسراً.

swift
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 مع تأثير التمويه فوق واجهة المستخدم بالكامل عند الذهاب إلى Inactive وإزالته عند العودة إلى Active. هذا يضمن أن البيانات السرية لن تكون مرئية في لقطات App Switcher وControl Center. بالمثل، يمكنك استخدام UIImageView مع شعار لتراكب ذي علامة تجارية.

الأسئلة الشائعة

هل يسبق Inactive دائماً Background؟

نعم. Inactive هي حالة وسيطة إلزامية قبل الانتقال إلى Background على iOS. لا يمكن للتطبيق الانتقال من Active مباشرة إلى Background — أولاً يصبح Inactive، ثم Background. على Android، بالمثل، يتم استدعاء onPause دائماً قبل onStop. هذا يعطي المطور فرصة لإعداد البيانات للحفظ قبل الذهاب بالكامل إلى الخلفية.

هل يمكن للتطبيق أن يصبح Inactive بدون مقاطعة؟

نعم. على iPad، عند تشغيل Slide Over أو Split View، يصبح المشهد النشط Inactive، على الرغم من عدم حدوث أي مقاطعة نظامية — المستخدم ببساطة يتفاعل مع مشهد آخر. هذه ميزة من iPadOS متعدد النوافذ. على iPhone، يتم تشغيل Inactive دائماً بواسطة مقاطعة نظامية — مكالمة، إشعار، Control Center أو Notification Center.

كم من الوقت يمكن للتطبيق البقاء في Inactive؟

عادة من 0.1 إلى 2 ثانية. أثناء مكالمة واردة مع شاشة الاتصال — حتى 30 ثانية (حتى يرد المستخدم أو يرفض المكالمة). iOS لا يحدد وقتاً في Inactive قسراً، لكن النظام قد ينهي التطبيق إذا لم يستجب للأحداث (watchdog). على Android، onPause ليس له حد زمني، لكن يُنصح بإكمال العمل خلال 200 مللي ثانية.

ما هو scenePhase في SwiftUI أثناء Inactive؟

ScenePhase.inactive — قيمة enum ScenePhase التي تُعيّن عندما يكون المشهد في المقدمة لكنه لا يستقبل أحداثاً. في SwiftUI، يمكنك مراقبته عبر @Environment(\.scenePhase) والتفاعل عبر onChange. عند الانتقال من .active إلى .inactive، أوقف المؤقتات والرسوم المتحركة. عند العودة إلى .active، استأنفها. عند الذهاب إلى .background، احفظ الحالة.

هل يجب إخفاء واجهة المستخدم أثناء Inactive لجميع التطبيقات؟

لا، فقط للتطبيقات التي تتعامل مع بيانات سرية: البنوك، الطبية، المؤسسية، وتطبيقات المراسلة مع المحادثات الخاصة. للألعاب وتطبيقات الترفيه، إخفاء واجهة المستخدم غير مطلوب. لكن إيقاف اللعبة والصوت أثناء Inactive ممارسة جيدة لتجنب تداخل الصوت مع إشعارات النظام. توصي Apple بإخفاء البيانات الحساسة لكنها لا تفرض ذلك.

الملخص

  • Inactive — حالة انتقالية بين Active وBackground، التطبيق مرئي لكنه غير تفاعلي
  • الأسباب — Control Center، Notification Center، مكالمة واردة، أذونات النظام، Siri، Apple Pay
  • المعالجة على iOS — applicationWillResignActive لإيقاف الرسوم المتحركة والوسائط وإخفاء البيانات الحساسة
  • SwiftUI — scenePhase .inactive عبر Environment، onChange للآثار الجانبية
  • Android — onPause كمكافئ، مع دعم النوافذ المتعددة والتغطية الجزئية لـ Activity
  • الأمان — تراكب التمويه أو FLAG_SECURE للحماية من لقطات App Switcher وControl Center
  • طبيعة مؤقتة — يستمر Inactive حتى انتهاء المقاطعة، ثم يعود إلى Active أو يذهب إلى Background

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا