Inactive — η ουσία, μεταβατική κατάσταση μεταξύ Active και Background

Συγγραφέας: IT Sectr Δημοσιεύτηκε: 2026-03-03 Χρόνος ανάγνωσης: 10 λεπ

Inactive — μεταβατική κατάσταση του κύκλου ζωής της εφαρμογής μεταξύ Active και Background, κατά την οποία η εφαρμογή είναι ορατή στην οθόνη, αλλά δεν λαμβάνει συμβάντα αφής. Εξηγούμε πώς δημιουργείται το Inactive σε iOS και Android, ποιες μέθοδοι delegate ευθύνονται για αυτό και πώς να χειριζόμαστε σωστά τις διακοπές — κλήσεις, ειδοποιήσεις και συστημικές χειρονομίες.

Κύρια σημεία

  • Inactive — η εφαρμογή είναι ορατή στην οθόνη, αλλά δεν δέχεται συμβάντα αφής λόγω συστημικής διακοπής
  • applicationWillResignActive — η κύρια μέθοδος που σηματοδοτεί τη μετάβαση σε Inactive στο iOS
  • Προσωρινός χαρακτήρας — το Inactive διαρκεί από κλάσματα δευτερολέπτου έως και λίγα δευτερόλεπτα, στη συνέχεια ακολουθεί μετάβαση σε Active ή Background
  • Αιτίες — εισερχόμενη κλήση, Control Center, Notification Center, συστημική ειδοποίηση, Apple Pay, Siri
  • SwiftUI — το scenePhase .inactive είναι ισοδύναμο με το Inactive στο UIKit

Inactive — η ουσία της μεταβατικής κατάστασης

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 καλύπτεται μερικώς από άλλο στοιχείο.

Σύγκριση Inactive σε iOS και Android

Στο iOS, το Inactive είναι μια ξεχωριστή κατάσταση του κύκλου ζωής της εφαρμογής (μία από τις πέντε: Not Running, Active, Inactive, Background, Suspended). Στο Android δεν υπάρχει άμεσο ανάλογο — το onPause σηματοδοτεί ότι το Activity χάνει την εστίαση εισόδου, αλλά μπορεί να παραμείνει ορατό (για παράδειγμα, κατά το άνοιγμα ενός διαλόγου). Η βασική διαφορά: το iOS Inactive είναι κατάσταση της εφαρμογής στο σύνολό της, το Android onPause είναι κατάσταση ενός συγκεκριμένου Activity. Σε λειτουργία multi-window στο Android, ένα Activity μπορεί να είναι σε onPause (χωρίς εστίαση) ενώ ένα άλλο σε onResume (με εστίαση).

ΧαρακτηριστικόiOS InactiveAndroid onPause
UI ορατόΝαιΝαι (μερικώς ή πλήρως)
Συμβάντα αφήςΔεν δέχεταιΔεν δέχεται
ΔιάρκειαΜέχρι το τέλος της διακοπήςΜέχρι την επιστροφή εστίασης ή μετάβαση σε φόντο
Επόμενη κατάστασηActive ή BackgroundonResume ή onStop
ΕπίπεδοΕφαρμογή (UIApplication)Activity
Multi-windowΜία σκηνή ενεργήΠολλά 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). Λειτουργία multi-window — το ανενεργό παράθυρο λαμβάνει 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 λαμβάνουν σκρινσότ UI
    }
}

Ο κώδικας δείχνει το χειρισμό του Inactive στο UIKit. Το applicationWillResignActive σταματά το βίντεο, σταματά τη συλλογή δεδομένων και κρύβει ευαίσθητες πληροφορίες. Αυτό είναι σημαντικό διότι κατά το άνοιγμα του Control Center ή App Switcher, το σύστημα λαμβάνει ένα σκρινσότ του τρέχοντος UI — ο χρήστης μπορεί να δει εμπιστευτικά δεδομένα στην προεπισκόπηση. Το NotificationCenter επιτρέπει στα στοιχεία της εφαρμογής να εγγραφούν σε συμβάντα διακοπής.

Inactive στο iOS: Swift και UIKit

Στο iOS, το Inactive χειρίζεται από ένα ζεύγος μεθόδων: applicationWillResignActive (μετάβαση σε Inactive) και applicationDidBecomeActive (επιστροφή από Inactive). Αυτές οι μέθοδοι είναι μέρος του UIApplicationDelegate και καλούνται για κάθε μετάβαση μέσω Inactive. Από το iOS 13 και το UISceneDelegate, προστέθηκαν τα sceneWillResignActive και sceneDidBecomeActive για σενάρια multi-window.

SceneDelegate και multi-window 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 για υποστήριξη multi-window. Κάθε σκηνή έχει ανεξάρτητη κατάσταση και ο προγραμματιστής πρέπει να τις χειρίζεται ξεχωριστά.

Inactive στο Android: onPause και ορατότητα

Στο Android, το άμεσο ανάλογο του iOS Inactive είναι η μέθοδος 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, απενεργοποιήστε το first responder (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 με εφέ θολώματος (blur) τοποθετείται πάνω από ολόκληρο το UI κατά τη μετάβαση σε 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, αν και δεν συμβαίνει καμία συστημική διακοπή — ο χρήστης απλώς αλληλεπιδρά με μια άλλη σκηνή. Αυτή είναι μια λειτουργία multi-window του iPadOS. Στο iPhone, το Inactive προκαλείται πάντα από μια συστημική διακοπή — κλήση, ειδοποίηση, Control Center ή Notification Center.

Πόσο μπορεί μια εφαρμογή να παραμείνει σε Inactive;

Συνήθως από 0.1 έως 2 δευτερόλεπτα. Σε μια εισερχόμενη κλήση με οθόνη κλήσης — έως 30 δευτερόλεπτα (μέχρι να απαντήσει ή να απορρίψει την κλήση ο χρήστης). Το iOS δεν περιορίζει υποχρεωτικά τον χρόνο σε Inactive, αλλά το σύστημα μπορεί να τερματίσει την εφαρμογή αν δεν ανταποκρίνεται σε συμβάντα (watchdog). Στο Android, το onPause δεν έχει χρονικό περιορισμό, αλλά συνιστάται η ολοκλήρωση της εργασίας εντός 200 ms.

Τι είναι το scenePhase στο SwiftUI κατά το Inactive;

ScenePhase.inactive — η τιμή του enum ScenePhase, που ορίζεται όταν η σκηνή βρίσκεται στο προσκήνιο αλλά δεν λαμβάνει συμβάντα. Στο SwiftUI, μπορείτε να το παρατηρήσετε μέσω @Environment(\.scenePhase) και να αντιδράσετε μέσω onChange. Κατά τη μετάβαση από .active σε .inactive, αναστείλτε χρονοδιακόπτες και κινητοποιήσεις. Κατά την επιστροφή σε .active — συνεχίστε. Κατά τη μετάβαση σε .background — αποθηκεύστε την κατάσταση.

Χρειάζεται να κρύβουμε το UI σε Inactive για όλες τις εφαρμογές;

Όχι, μόνο για εφαρμογές που εργάζονται με εμπιστευτικά δεδομένα: τραπεζικές, ιατρικές, εταιρικές, messengers με ιδιωτικές συνομιλίες. Για παιχνίδια και εφαρμογές ψυχαγωγίας, η απόκρυψη UI δεν απαιτείται. Ωστόσο, η αναστολή του παιχνιδιού και του ήχου σε Inactive είναι καλή πρακτική για να αποφευχθεί η επικάλυψη ήχων με συστημικές ειδοποιήσεις. Η Apple συνιστά την απόκρυψη ευαίσθητων δεδομένων, αλλά δεν το απαιτεί.

Σύνοψη

  • Inactive — μεταβατική κατάσταση μεταξύ Active και Background, εφαρμογή ορατή αλλά μη διαδραστική
  • Αιτίες — Control Center, Notification Center, εισερχόμενη κλήση, συστημικές άδειες, Siri, Apple Pay
  • Χειρισμός iOS — applicationWillResignActive για αναστολή κινητοποιήσεων, μέσων και απόκρυψη ευαίσθητων δεδομένων
  • SwiftUI — scenePhase .inactive μέσω Environment, onChange για παρενέργειες
  • Android — onPause ως ανάλογο, με υποστήριξη multi-window και μερικής κάλυψης Activity
  • Ασφάλεια — επικάλυψη blur ή FLAG_SECURE για προστασία από σκρινσότ App Switcher και Control Center
  • Προσωρινός χαρακτήρας — Inactive διαρκεί μέχρι το τέλος της διακοπής, στη συνέχεια επιστροφή σε Active ή μετάβαση σε Background

Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση

Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.

Συζήτηση έργου

Διαβάστε επίσης