Deferred Navigation — τι είναι, αρχές και υλοποίηση

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

Το Deferred Navigation είναι ένα μοτίβο αναβληθείσας πλοήγησης κατά το οποίο η μετάβαση στην επόμενη οθόνη γίνεται μετά την ολοκλήρωση μιας ασύγχρονης λειτουργίας, όχι απευθείας τη στιγμή της ενέργειας του χρήστη. Σύμφωνα με το Android Developers (2024), το deferred navigation επιτρέπει την αποφυγή συνθήκης ανταγωνισμού μεταξύ πλοήγησης και φόρτωσης δεδομένων, καθώς και απλοποιεί τον χειρισμό μεταβάσεων από push ειδοποιήσεις και Deeplink. Η βασική διαφορά — η διαδρομή υπολογίζεται αφού όλα τα απαραίτητα δεδομένα είναι διαθέσιμα.

Κύρια σημεία

  • Deferred Navigation — αναβληθείσα πλοήγηση, όπου η μετάβαση εκτελείται μετά την ολοκλήρωση ασύγχρονων λειτουργιών
  • Άμεση πλοήγηση επεξεργάζεται τη μετάβαση άμεσα, deferred — περιμένει δεδομένα
  • Τυπικά σενάρια: εξουσιοδότηση, Deeplink, push ειδοποιήσεις, φόρτωση διαμόρφωσης
  • Στο Android υλοποιείται μέσω Navigation Component με callbacks και StateFlow
  • Στο iOS χρησιμοποιείται Combine ή async/await με συντονιστή πλοήγησης

Τι είναι το Deferred Navigation

Deferred Navigation είναι ένα αρχιτεκτονικό μοτίβο κατά το οποίο η απόφαση πλοήγησης αναβάλλεται μέχρι τη στιγμή που όλα τα απαραίτητα δεδομένα γίνουν διαθέσιμα. Σε αντίθεση με την άμεση μετάβαση, όπου ο χρήστης πατάει ένα κουμπί και μπαίνει αμέσως σε μια νέα οθόνη, το deferred navigation διαχωρίζει το γεγονός-ενεργοποιητή από την πραγματική μετάβαση, προσθέτοντας μια ασύγχρονη λειτουργία μεταξύ τους.

Αρχιτεκτονικά, το Deferred Navigation βασίζεται στην αλλαγή κατάστασης: το πάτημα ενός κουμπιού ξεκινά μια ασύγχρονη διαδικασία και η εγγραφή στο αποτέλεσμά της ενεργοποιεί την πλοήγηση. Αυτό είναι ιδιαίτερα σημαντικό σε εφαρμογές με αρχιτεκτονική MVVM ή MVI, όπου το ViewModel διαχειρίζεται την κατάσταση και το View (Activity, Fragment, SwiftUI View) εγγράφεται στις αλλαγές και αντιδρά με μετάβαση. Αυτή η προσέγγιση εξαλείφει την άμεση εξάρτηση μεταξύ UI και λογικής πλοήγησης.

Σύμφωνα με το Google I/O 2023, το deferred navigation συνιστάται για όλα τα σενάρια όπου η πλοήγηση εξαρτάται από το αποτέλεσμα ενός αιτήματος δικτύου, ελέγχου εξουσιοδότησης, φόρτωσης διαμόρφωσης ή αδειών. Το μοτίβο είναι επίσης υποχρεωτικό κατά την επεξεργασία Deeplink, όταν η εφαρμογή πρέπει πρώτα να εκκινηθεί, να φορτώσει την αρχική οθόνη και μόνο στη συνέχεια να μεταβεί στη διαδρομή-στόχο.

Πότε χρειάζεται αναβληθείσα πλοήγηση

Deferred Navigation εφαρμόζεται σε σενάρια όπου η άμεση μετάβαση οδηγεί σε εσφαλμένη κατάσταση οθόνης ή σφάλματα φόρτωσης. Ας εξετάσουμε τέσσερις κύριες περιπτώσεις όπου το deferred navigation είναι υποχρεωτικό.

Εξουσιοδότηση και αυθεντικοποίηση

Εάν ο χρήστης κάνει κλικ σε προστατευόμενο περιεχόμενο, η εφαρμογή πρέπει πρώτα να ελέγξει το διακριτικό πρόσβασης. Η άμεση πλοήγηση στην οθόνη περιεχομένου θα οδηγήσει σε κενή οθόνη ή σφάλμα 401 εάν το διακριτικό έχει λήξει. Το Deferred navigation ελέγχει το διακριτικό και μόνο σε περίπτωση επιτυχίας μεταβαίνει στην οθόνη-στόχο. Σε περίπτωση αποτυχίας ανακατευθύνει στην οθόνη σύνδεσης.

Επεξεργασία Deeplink

Όταν η εφαρμογή ανοίγει μέσω ενός εξωτερικού συνδέσμου, πρώτα πρέπει να φορτωθεί η αρχική οθόνη, να αποκατασταθεί η κατάσταση πλοήγησης και μόνο στη συνέχεια να εκτελεστεί η μετάβαση μέσω Deeplink. Η άμεση πλοήγηση στην οθόνη-στόχο χωρίς αρχικό πλαίσιο θα οδηγήσει σε ανωμαλίες: κενή στοίβα πλοήγησης ή κατεστραμμένο back stack.

Push ειδοποιήσεις με περιεχόμενο

Κατά το πάτημα μιας push ειδοποίησης, η εφαρμογή μπορεί να βρίσκεται σε τρεις καταστάσεις: κλειστή, σε φόντο ή ενεργή. Το Deferred Navigation καθορίζει την κατάσταση της εφαρμογής, φορτώνει το απαραίτητο περιεχόμενο και μόνο στη συνέχεια εμφανίζει την οθόνη-στόχο. Το iOS επιτρέπει την επεξεργασία αυτού του σεναρίου μέσω UNNotificationContentExtension.

Δυναμικό feature flag

Εάν η λειτουργικότητα της οθόνης ελέγχεται από ένα feature flag από τον διακομιστή, το deferred navigation επιτρέπει πρώτα να ζητηθεί η διαμόρφωση και μόνο στη συνέχεια να εμφανιστεί η οθόνη. Εάν η λειτουργία είναι απενεργοποιημένη, ο χρήστης βλέπει εναλλακτικό περιεχόμενο ή placeholder αντί για κενή οθόνη.

ΣενάριοΆμεση πλοήγησηDeferred Navigation
ΕξουσιοδότησηΚενή οθόνη σε ληγμένο διακριτικόΑνακατεύθυνση στη σύνδεση
DeeplinkΚατεστραμμένο back stackΣωστή στοίβα πλοήγησης
PushΦόρτωση χωρίς πλαίσιοΔεδομένα έτοιμα πριν τη μετάβαση
Feature flagΕμφάνιση μη διαθέσιμης λειτουργίαςPlaceholder ή εναλλακτική

Deferred Navigation vs άμεση πλοήγηση

Η άμεση πλοήγηση (direct navigation) είναι μια παραδοσιακή προσέγγιση κατά την οποία η μετάβαση εκτελείται αμέσως ως απάντηση σε ένα γεγονός. Ο χρήστης πατάει ένα κουμπί και ο δρομολογητής UI αλλάζει αμέσως οθόνη. Αυτή η προσέγγιση είναι απλή και προβλέψιμη, αλλά περιορισμένη σε σενάρια όπου απαιτούνται δεδομένα από τον διακομιστή ή έλεγχος συνθηκών.

Το Deferred Navigation προσθέτει ένα επίπεδο με τη μορφή ασύγχρονης κατάστασης. Το γεγονός του χρήστη ξεκινά μια λειτουργία και η εγγραφή στο αποτέλεσμα διαχειρίζεται την πλοήγηση. Αυτό αυξάνει την πολυπλοκότητα του κώδικα, αλλά παρέχει ευελιξία: το ίδιο έναυσμα μπορεί να οδηγήσει σε διαφορετικές οθόνες ανάλογα με τα φορτωμένα δεδομένα.

Η επιλογή μεταξύ δύο προσεγγίσεων εξαρτάται από τις απαιτήσεις: εάν για την εμφάνιση της οθόνης δεν απαιτούνται ασύγχρονα δεδομένα — χρησιμοποιήστε άμεση πλοήγηση. Εάν η οθόνη εξαρτάται από το αποτέλεσμα ενός αιτήματος, εξουσιοδότησης ή εξωτερικών συνθηκών — το deferred navigation είναι υποχρεωτικό. Η υβριδική προσέγγιση, όπου μέρος των μεταβάσεων είναι άμεσο και μέρος αναβληθέν, είναι η πιο διαδεδομένη πρακτική σε βιομηχανικές εφαρμογές.

Υλοποίηση στο Android: Navigation Component και ViewModel

Το Android Jetpack παρέχει μηχανισμούς για την υλοποίηση του Deferred Navigation σε αρχιτεκτονικό επίπεδο. Η βασική ιδέα — το ViewModel διαχειρίζεται την κατάσταση και το Activity ή Fragment εγγράφεται στις αλλαγές και ενεργοποιεί την πλοήγηση μέσω NavController.

Deferred Navigation με StateFlow

Το StateFlow στα Kotlin coroutines είναι το ιδανικό εργαλείο για deferred navigation. Το ViewModel ενημερώνει το StateFlow με το συμβάν πλοήγησης και το Activity το παρατηρεί και εκτελεί τη μετάβαση. Όταν το συμβάν υποβληθεί σε επεξεργασία, το StateFlow καθαρίζεται, αποτρέποντας την επαναλαμβανόμενη πλοήγηση.

kotlin
class MainViewModel : ViewModel() {
    private val _navigation = MutableSharedFlow<NavigationEvent>()
    val navigation: SharedFlow<NavigationEvent> = _navigation

    fun onDeepLinkReceived(link: String) {
        viewModelScope.launch {
            val data = resolveDeepLink(link)
            _navigation.emit(NavigationEvent.GoToScreen(data))
        }
    }
}

Deferred Navigation με NavController

Στο Activity, η εγγραφή στην πλοήγηση ενεργοποιεί το NavController με τη διαδρομή από το ViewModel. Για την αποτροπή επαναλαμβανόμενης πλοήγησης κατά την περιστροφή οθόνης, χρησιμοποιείται το NavigationEventWrapper που επεξεργάζεται το συμβάν μόνο μία φορά. Το Jetpack Navigation 2.7+ υποστηρίζει Safe Args για ασφαλή μετάδοση ορισμάτων ως προς τον τύπο.

kotlin
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val vm: MainViewModel by viewModels()
        repeatOnLifecycle(Lifecycle.State.STARTED) {
            vm.navigation.collect { event ->
                when (event) {
                    is NavigationEvent.GoToScreen ->
                        findNavController(R.id.nav_host)
                            .navigate(event.route)
                }
            }
        }
    }
}

Υλοποίηση στο iOS: Coordinator Pattern και Combine

Το iOS δεν διαθέτει ενσωματωμένο Navigation Component ανάλογο του Android Jetpack, επομένως οι προγραμματιστές υλοποιούν το Deferred Navigation μέσω Coordinator Pattern σε συνδυασμό με Combine ή async/await. Ο Coordinator διαχειρίζεται τη στοίβα οθονών και λαμβάνει αποφάσεις πλοήγησης βάσει των φορτωμένων δεδομένων.

Coordinator με Combine

Ο Coordinator είναι ένα αντικείμενο που διαχειρίζεται την πλοήγηση μεταξύ ViewController. Σε συνδυασμό με το Combine, το ViewModel δημοσιεύει συμβάντα μέσω PassthroughSubject και ο Coordinator εγγράφεται σε αυτά και εκτελεί τη μετάβαση. Αυτή η προσέγγιση διαχωρίζει πλήρως το UI από τη λογική πλοήγησης και συμμορφώνεται με τις συστάσεις της Apple για αρχιτεκτονική εφαρμογών.

swift
final class AppCoordinator {
    private var cancellables = Set<AnyCancellable>()

    func start(viewModel: MainViewModel) {
        viewModel.$navigationDestination
            .compactMap { $0 }
            .sink { [weak self] destination in
                self?.navigateTo(destination)
            }
            .store(in: &cancellables)
    }
}

Deferred Navigation με async/await

Το Swift 5.5 εισήγαγε το structured concurrency, το οποίο επιτρέπει την υλοποίηση αναβληθείσας πλοήγησης μέσω async/await χωρίς Combine. Το ViewModel παρέχει μια async συνάρτηση που επιστρέφει τη διαδρομή μετά τη φόρτωση δεδομένων. Ο Coordinator καλεί αυτή τη συνάρτηση σε ένα Task και εκτελεί τη μετάβαση σύμφωνα με τη λαμβανόμενη διαδρομή.

swift
class AuthViewModel: ObservableObject {
    func resolveDeeplink(_ url: URL) async -> AppRoute? {
        guard let token = await AuthService.shared.getValidToken() else { return .login }
        return await DeeplinkRouter.resolve(url, token: token)
    }
}

// Στον Coordinator:
Task {
    if let route = await viewModel.resolveDeeplink(url) {
        navigateTo(route)
    }
}

Συνηθισμένα λάθη και best practices

Το Deferred Navigation απλοποιεί τον χειρισμό ασύγχρονων σεναρίων, αλλά απαιτεί πειθαρχημένη προσέγγιση στη διαχείριση κατάστασης. Ας εξετάσουμε τα κύρια λάθη που κάνουν οι προγραμματιστές κατά την υλοποίηση του deferred navigation.

Λάθος: πλοήγηση πριν την ολοκλήρωση αρχικοποίησης

Το πιο συνηθισμένο λάθος — προσπάθεια εκτέλεσης deferred navigation πριν η αρχική οθόνη αρχικοποιηθεί πλήρως και ο NavController ή Coordinator είναι έτοιμος για μετάβαση. Στο Android αυτό οδηγεί σε IllegalStateException, στο iOS — σε απροσδιόριστη κατάσταση UI. Λύση — βεβαιωθείτε ότι ο κύκλος ζωής του στοιχείου βρίσκεται σε κατάσταση STARTED ή RESUMED πριν από την έναρξη της πλοήγησης.

Λάθος: επαναλαμβανόμενη πλοήγηση κατά την επιστροφή στην οθόνη

Εάν το StateFlow ή Subject δεν καθαρίζει το συμβάν μετά την επεξεργασία, κατά την επιστροφή στην προηγούμενη οθόνη ο χρήστης μπορεί να ανακατευθυνθεί αυτόματα ξανά στην ίδια οθόνη. Χρησιμοποιήστε SharedFlow με replay=0 στο Android ή CurrentValueSubject με nil μετά την επεξεργασία στο iOS, ώστε το συμβάν πλοήγησης να ενεργοποιείται μόνο μία φορά.

Best Practice: ενιαίος NavController ή Coordinator

Χρησιμοποιήστε ένα κεντρικό στοιχείο για ολόκληρη την πλοήγηση στην εφαρμογή. Όταν κάθε Activity, Fragment ή ViewController έχει τον δικό του ελεγκτή πλοήγησης, το deferred navigation μεταξύ διαφορετικών τμημάτων της εφαρμογής γίνεται χαοτικό. Ένας ενιαίος Coordinator διευκολύνει τον εντοπισμό σφαλμάτων και τη δοκιμή σεναρίων πλοήγησης.

Best Practice: δοκιμή σεναρίων αναβληθείσας πλοήγησης

Το Deferred Navigation είναι πιο δύσκολο να δοκιμαστεί από την άμεση πλοήγηση, καθώς οι ασύγχρονες λειτουργίες εισάγουν παράγοντα χρόνου. Χρησιμοποιήστε TestDispatcher στο Android (kotlinx-coroutines-test) και XCTestExpectation στο iOS για προσομοίωση φόρτωσης δεδομένων και ελέγξτε ότι η πλοήγηση εκτελείται σύμφωνα με την αναμενόμενη διαδρομή. Κάντε mock τις υπηρεσίες εξουσιοδότησης και deeplink για απομονωμένη δοκιμή κάθε σεναρίου.

Συχνές ερωτήσεις

Σε τι διαφέρει το Deferred Navigation από το Deep Link;

Deferred Navigation είναι ένα μοτίβο αναβληθείσας μετάβασης που μπορεί να εφαρμοστεί σε οποιοδήποτε ασύγχρονο σενάριο. Το Deep Link είναι ένα από τα ερεθίσματα για το deferred navigation, αλλά όχι το μοναδικό. Η εξουσιοδότηση και τα feature flags χρησιμοποιούν επίσης αναβληθείσα πλοήγηση.

Μπορούν να συνδυαστούν το deferred και το direct navigation;

Ναι, οι περισσότερες εφαρμογές χρησιμοποιούν υβριδική προσέγγιση. Η οθόνη λίστας προϊόντων (χωρίς ασύγχρονες εξαρτήσεις) μπορεί να χρησιμοποιεί άμεση πλοήγηση, ενώ η οθόνη λεπτομερειών με φόρτωση δεδομένων — deferred. Ο διαχωρισμός καθορίζεται από την αρχιτεκτονική της συγκεκριμένης οθόνης.

Πώς να αποφευχθεί η συνθήκη ανταγωνισμού στο deferred navigation;

Χρησιμοποιήστε SharedFlow χωρίς replay στο Android και combineLatest χωρίς προσωρινή αποθήκευση στο iOS. Ακυρώστε προηγούμενες εγγραφές σε νέο έναυσμα. Αυτό εγγυάται ότι μόνο το τελευταίο συμβάν πλοήγησης υποβάλλεται σε επεξεργασία.

Υποστηρίζει το Jetpack Compose το deferred navigation;

Ναι, στο Compose το deferred navigation υλοποιείται μέσω εγγραφής στο StateFlow του ViewModel και κλήσης του NavController.navigate σε LaunchedEffect. Η Google συνιστά τη χρήση του Navigation Compose με μοντέλο συμβάντων για deferred σενάρια.

Πώς να χειριστείτε το deferred navigation όταν η εφαρμογή είναι ελαχιστοποιημένη;

Αναβάλετε την πλοήγηση μέχρι η εφαρμογή να γίνει ξανά ενεργή. Στο Android χρησιμοποιήστε το Lifecycle.State.STARTED για φιλτράρισμα συμβάντων. Στο iOS ελέγξτε το UIApplication.State στο μπλοκ Combine ή async/await.

Σύνοψη

  • Deferred Navigation — μοτίβο όπου η μετάβαση εκτελείται μετά την ολοκλήρωση μιας ασύγχρονης λειτουργίας
  • Κύρια σενάρια: εξουσιοδότηση, Deeplink, push ειδοποιήσεις, feature flags
  • StateFlow/SharedFlow στο Android και Combine/async-await στο iOS — εργαλεία υλοποίησης
  • Deferred Navigation σε αντίθεση με την άμεση δεν προκαλεί συνθήκη ανταγωνισμού σε ασύγχρονη φόρτωση
  • Navigation Component στο Android και Coordinator Pattern στο iOS — βασικές αρχιτεκτονικές
  • Ενιαίος NavController/Coordinator είναι υποχρεωτικός για συνεπή πλοήγηση
  • Δοκιμή deferred navigation απαιτεί TestDispatcher και mocks για ασύγχρονες υπηρεσίες

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

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

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

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