Deferred Navigation — mi ez, elvek és megvalósítás

Szerző: IT Sectr Megjelenés: 2026-06-10 Olvasási idő: 9 perc

A Deferred Navigation egy halasztott navigációs minta, amelyben a következő képernyőre való átlépés az aszinkron művelet befejezése után történik, nem közvetlenül a felhasználói akció pillanatában. A Android Developers (2024) szerint a deferred navigation lehetővé teszi a versenyhelyzet elkerülését a navigáció és az adatbetöltés között, valamint egyszerűsíti a push értesítésekből és Deeplinkből érkező átmenetek kezelését. A kulcsfontosságú különbség — az útvonal kiszámítása az összes szükséges adat rendelkezésre állása után történik.

Főbb pontok

  • Deferred Navigation — halasztott navigáció, ahol az átmenet az aszinkron műveletek befejezése után történik
  • Közvetlen navigáció azonnal feldolgozza az átmenetet, a deferred — megvárja az adatokat
  • Tipikus forgatókönyvek: hitelesítés, Deeplink, push értesítések, konfiguráció betöltése
  • Androidban Navigation Component segítségével, callbackekkel és StateFlow-val valósul meg
  • iOS-ben Combine vagy async/await használatos navigációs koordinátorral

Mi az a Deferred Navigation

A Deferred Navigation egy architekturális minta, amelyben a navigációs döntés addig halasztódik, amíg az összes szükséges adat elérhetővé nem válik. Ellentétben a közvetlen átmenettel, ahol a felhasználó megnyom egy gombot és azonnal egy új képernyőre kerül, a deferred navigation elválasztja az esemény-triggert és a tényleges átmenetet, aszinkron műveletet iktatva közéjük.

Architekturálisan a Deferred Navigation állapotváltozáson alapul: a gomb megnyomása elindít egy aszinkron folyamatot, és az eredményre való feliratkozás elindítja a navigációt. Ez különösen fontos az MVVM vagy MVI architektúrájú alkalmazásokban, ahol a ViewModel kezeli az állapotot, a View (Activity, Fragment, SwiftUI View) pedig feliratkozik a változásokra és átmenettel reagál. Ez a megközelítés megszünteti a közvetlen függőséget a UI és a navigációs logika között.

A Google I/O 2023 adatai szerint a deferred navigation minden olyan forgatókönyvhöz ajánlott, ahol a navigáció hálózati kérés eredményétől, hitelesítési ellenőrzéstől, konfigurációbetöltéstől vagy engedélyektől függ. A minta kötelező a Deeplink feldolgozásakor is, amikor az alkalmazásnak először el kell indulnia, be kell töltenie a gyökérképernyőt, és csak azután léphet a célútvonalra.

Mikor van szükség halasztott navigációra

A Deferred Navigation olyan forgatókönyvekben alkalmazandó, ahol a közvetlen átmenet a képernyő helytelen állapotához vagy betöltési hibákhoz vezet. Nézzük meg azt a négy fő esetet, amikor a deferred navigation kötelező.

Hitelesítés és azonosítás

Ha a felhasználó védett tartalomra kattint, az alkalmazásnak először ellenőriznie kell a hozzáférési tokent. A közvetlen navigáció a tartalomképernyőre üres képernyőhöz vagy 401-es hibához vezet, ha a token lejárt. A Deferred navigation ellenőrzi a tokent, és csak siker esetén lép a célképernyőre. Sikertelenség esetén átirányít a bejelentkezési képernyőre.

Deeplink feldolgozása

Amikor az alkalmazás egy külső hivatkozáson keresztül nyílik meg, először be kell tölteni a gyökérképernyőt, helyre kell állítani a navigációs állapotot, és csak azután végrehajtani az átmenetet a Deeplinken keresztül. A közvetlen navigáció a célképernyőre gyökérkontextus nélkül anomáliákhoz vezet: üres navigációs veremhez vagy sérült back stackhez.

Push értesítések tartalommal

Egy push értesítésre koppintva az alkalmazás három állapotban lehet: zárt, háttérben vagy aktív. A Deferred Navigation meghatározza az alkalmazás állapotát, betölti a szükséges tartalmat, és csak azután jeleníti meg a célképernyőt. Az iOS lehetővé teszi ennek a forgatókönyvnek a feldolgozását az UNNotificationContentExtension segítségével.

Dinamikus feature flag

Ha a képernyő funkcióit egy szerveroldali feature flag vezérli, a deferred navigation lehetővé teszi a konfiguráció előzetes lekérését és csak azután a képernyő megjelenítését. Ha a funkció ki van kapcsolva, a felhasználó alternatív tartalmat vagy helyettesítőt lát üres képernyő helyett.

ForgatókönyvKözvetlen navigációDeferred Navigation
HitelesítésÜres képernyő lejárt tokennélÁtirányítás a bejelentkezéshez
DeeplinkSérült back stackHelyes navigációs verem
PushBetöltés kontextus nélkülAdatok készen az átmenet előtt
Feature flagNem elérhető funkció megjelenítéseHelyettesítő vagy alternatíva

Deferred Navigation vs közvetlen navigáció

A közvetlen navigáció (direct navigation) egy hagyományos megközelítés, amelyben az átmenet azonnal megtörténik egy eseményre válaszul. A felhasználó megnyom egy gombot, és az UI router azonnal képernyőt vált. Ez a megközelítés egyszerű és kiszámítható, de korlátozott azokban a forgatókönyvekben, ahol szerveradatokat vagy feltételek ellenőrzését igényel.

A Deferred Navigation egy réteget ad hozzá aszinkron állapot formájában. A felhasználói esemény elindít egy műveletet, és az eredményre való feliratkozás vezérli a navigációt. Ez növeli a kód komplexitását, de rugalmasságot biztosít: ugyanaz a trigger különböző képernyőkre vezethet a betöltött adatoktól függően.

A választás a két megközelítés között a követelményektől függ: ha a képernyő megjelenítéséhez nincs szükség aszinkron adatokra, használjon közvetlen navigációt. Ha a képernyő egy kérés eredményétől, hitelesítéstől vagy külső feltételektől függ, a deferred navigation kötelező. A hibrid megközelítés, ahol az átmenetek egy része közvetlen, egy része pedig halasztott, a legelterjedtebb gyakorlat a termelési alkalmazásokban.

Megvalósítás Androidban: Navigation Component és ViewModel

Az Android Jetpack mechanizmusokat biztosít a Deferred Navigation architekturális szintű megvalósításához. A fő ötlet — a ViewModel kezeli az állapotot, az Activity vagy Fragment pedig feliratkozik a változásokra és elindítja a navigációt a NavControlleren keresztül.

Deferred Navigation StateFlow-val

A StateFlow a Kotlin coroutines-ben ideális eszköz a deferred navigationhoz. A ViewModel frissíti a StateFlow-t a navigációs eseménnyel, az Activity pedig megfigyeli azt és végrehajtja az átmenetet. Az esemény feldolgozása után a StateFlow törlődik, megakadályozva az ismételt navigációt.

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 NavControllerrel

Az Activity-ben a navigációra való feliratkozás elindítja a NavControllert a ViewModel útvonalával. A képernyőelforgatáskor történő ismételt navigáció megelőzésére NavigationEventWrapper használatos, amely az eseményt csak egyszer dolgozza fel. A Jetpack Navigation 2.7+ támogatja a Safe Args-ot a típusbiztos argumentumátadáshoz.

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)
                }
            }
        }
    }
}

Megvalósítás iOS-ben: Coordinator Pattern és Combine

Az iOS nem rendelkezik beépített Navigation Componenttel, amely az Android Jetpackhez hasonlítana, ezért a fejlesztők a Deferred Navigation-ot a Coordinator Pattern segítségével valósítják meg Combine vagy async/await használatával. A Coordinator kezeli a képernyővermet és a betöltött adatok alapján hoz navigációs döntéseket.

Coordinator Combine-nal

A Coordinator egy objektum, amely a ViewControllerek közötti navigációt kezeli. A Combine-nal együttműködve a ViewModel eseményeket tesz közzé a PassthroughSubjecten keresztül, a Coordinator pedig feliratkozik rájuk és végrehajtja az átmenetet. Ez a megközelítés teljesen elkülöníti a UI-t a navigációs logikától, és megfelel az Apple alkalmazásarchitektúrára vonatkozó ajánlásainak.

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-tel

A Swift 5.5 bevezette a structured concurrencet, amely lehetővé teszi a halasztott navigáció megvalósítását async/await segítségével Combine nélkül. A ViewModel egy async függvényt biztosít, amely az adatok betöltése után visszaadja az útvonalat. A Coordinator ezt a függvényt egy Task-ban hívja meg, és a kapott útvonal szerint hajtja végre az átmenetet.

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)
    }
}

// A Coordinator-ban:
Task {
    if let route = await viewModel.resolveDeeplink(url) {
        navigateTo(route)
    }
}

Gyakori hibák és best practices

A Deferred Navigation egyszerűsíti az aszinkron forgatókönyvek kezelését, de fegyelmezett állapotkezelést igényel. Nézzük meg a fejlesztők által a deferred navigation bevezetésekor elkövetett főbb hibákat.

Hiba: navigáció az inicializálás befejezése előtt

A leggyakoribb hiba — a deferred navigation végrehajtásának megkísérlése, mielőtt a gyökérképernyő teljesen inicializálódott és a NavController vagy Coordinator készen áll az átmenetre. Androidban ez IllegalStateException-hez, iOS-ben pedig meghatározatlan UI-állapothoz vezet. Megoldás — győződjön meg róla, hogy a komponens életciklusa STARTED vagy RESUMED állapotban van a navigáció elindítása előtt.

Hiba: ismételt navigáció a képernyőre való visszatéréskor

Ha a StateFlow vagy Subject nem törli az eseményt a feldolgozás után, a korábbi képernyőre visszatérve a felhasználó automatikusan ugyanarra a képernyőre irányítható át újra. Használjon SharedFlow-t replay=0 beállítással Androidban vagy CurrentValueSubject-et nil értékkel a feldolgozás után iOS-ben, hogy a navigációs esemény csak egyszer aktiválódjon.

Best Practice: egyetlen NavController vagy Coordinator

Használjon egy központi komponenst a teljes navigációhoz az alkalmazásban. Amikor minden Activity, Fragment vagy ViewController saját navigációs vezérlővel rendelkezik, a deferred navigation az alkalmazás különböző részei között kaotikussá válik. Egyetlen Coordinator megkönnyíti a navigációs forgatókönyvek hibakeresését és tesztelését.

Best Practice: halasztott navigációs forgatókönyvek tesztelése

A Deferred Navigation tesztelése nehezebb, mint a közvetlen navigációé, mivel az aszinkron műveletek időtényezőt vezetnek be. Használjon TestDispatcheret Androidban (kotlinx-coroutines-test) és XCTestExpectationt iOS-ben az adatbetöltés szimulálásához, és ellenőrizze, hogy a navigáció a várt útvonalon történik-e. Mockolja a hitelesítési és deeplink szolgáltatásokat az egyes forgatókönyvek izolált teszteléséhez.

Gyakran ismételt kérdések

Miben különbözik a Deferred Navigation a Deep Linktől?

A Deferred Navigation egy halasztott átmeneti minta, amely bármilyen aszinkron forgatókönyvben alkalmazható. A Deep Link az egyik trigger a deferred navigation számára, de nem az egyetlen. A hitelesítés és a feature flag-ek is használnak halasztott navigációt.

Kombinálható a deferred és a direct navigation?

Igen, a legtöbb alkalmazás hibrid megközelítést használ. A terméklista képernyő (aszinkron függőségek nélkül) használhat közvetlen navigációt, az adatbetöltéssel rendelkező részletképernyő pedig deferred-t. A felosztást az adott képernyő architektúrája határozza meg.

Hogyan kerülhető el a versenyhelyzet a deferred navigation során?

Használjon SharedFlow-t replay nélkül Androidban és combineLatest-et pufferelés nélkül iOS-ben. Szüntesse meg a korábbi feliratkozásokat új trigger esetén. Ez garantálja, hogy csak az utolsó navigációs esemény kerül feldolgozásra.

Támogatja a Jetpack Compose a deferred navigationt?

Igen, Compose-ban a deferred navigation a ViewModel StateFlow-jára való feliratkozással és a NavController.navigate meghívásával valósítható meg LaunchedEffect-ben. A Google a Navigation Compose használatát ajánlja esemény-modellel a deferred forgatókönyvekhez.

Hogyan kezelendő a deferred navigation minimalizált alkalmazás esetén?

Halassza el a navigációt addig, amíg az alkalmazás újra aktívvá nem válik. Androidban használja a Lifecycle.State.STARTED-et az események szűréséhez. iOS-ben ellenőrizze az UIApplication.State-et a Combine vagy async/await blokkban.

Összefoglalás

  • Deferred Navigation — minta, ahol az átmenet egy aszinkron művelet befejezése után történik
  • Fő forgatókönyvek: hitelesítés, Deeplink, push értesítések, feature flag-ek
  • StateFlow/SharedFlow Androidban és Combine/async-await iOS-ben — megvalósítási eszközök
  • Deferred Navigation a közvetlennel ellentétben nem okoz versenyhelyzetet aszinkron betöltésnél
  • Navigation Component Androidban és Coordinator Pattern iOS-ben — alap architektúrák
  • Egyetlen NavController/Coordinator kötelező a konzisztens navigációhoz
  • Tesztelés — a deferred navigation TestDispatcheret és mockokat igényel az aszinkron szolgáltatásokhoz

Kulcsrakész mobilalkalmazást fejlesztünk

Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.

Projekt megbeszélése

Olvassa el is