Ang Deferred Navigation ay isang pattern ng naantalang nabigasyon kung saan ang paglipat sa susunod na screen ay nagaganap pagkatapos makumpleto ang isang asynchronous na operasyon, hindi direkta sa sandali ng aksyon ng gumagamit. Ayon sa Android Developers (2024), pinapayagan ng deferred navigation na maiwasan ang race condition sa pagitan ng nabigasyon at pag-load ng data, pati na rin pinapasimple ang paghawak ng mga transition mula sa mga push notification at Deeplink. Ang pangunahing pagkakaiba — ang ruta ay kinakalkula pagkatapos maging available ang lahat ng kinakailangang data.
Mga pangunahing punto
Deferred Navigation ay isang architectural pattern kung saan ang desisyon sa nabigasyon ay ipinagpapaliban hanggang sa maging available ang lahat ng kinakailangang data. Hindi tulad ng direktang transition, kung saan pinindot ng gumagamit ang isang button at agad na napupunta sa isang bagong screen, pinaghihiwalay ng deferred navigation ang trigger event at ang aktwal na transition, nagdaragdag ng asynchronous na operasyon sa pagitan ng mga ito.
Sa arkitektura, ang Deferred Navigation ay batay sa pagbabago ng estado: ang pagpindot ng button ay nagpapasimula ng asynchronous na proseso, at ang subscription sa resulta nito ay nagpapatakbo ng nabigasyon. Ito ay lalong mahalaga sa mga application na may arkitekturang MVVM o MVI, kung saan ang ViewModel ay namamahala ng estado, at ang View (Activity, Fragment, SwiftUI View) ay nag-subscribe sa mga pagbabago at tumutugon sa pamamagitan ng transition. Ang ganitong approach ay nag-aalis ng direktang dependency sa pagitan ng UI at ng nabigasyong lohika.
Ayon sa Google I/O 2023, ang deferred navigation ay inirerekomenda para sa lahat ng senaryo kung saan ang nabigasyon ay nakadepende sa resulta ng network request, pagsusuri ng awtorisasyon, pag-load ng configuration o mga pahintulot. Ang pattern ay sapilitan din sa pagproseso ng Deeplink, kapag ang application ay kailangan munang magsimula, i-load ang root screen at pagkatapos lamang pumunta sa target na ruta.
Deferred Navigation ay inilalapat sa mga senaryo kung saan ang direktang transition ay humahantong sa hindi tamang estado ng screen o mga error sa pag-load. Isaalang-alang natin ang apat na pangunahing kaso kung kailan sapilitan ang deferred navigation.
Kung ang gumagamit ay nag-click sa protektadong nilalaman, dapat munang suriin ng application ang access token. Direktang nabigasyon sa screen ng nilalaman ay hahantong sa isang blangkong screen o error 401 kung ang token ay nag-expire. Sinusuri ng Deferred navigation ang token, at kung matagumpay lamang — pumupunta sa target na screen. Kung nabigo — nagre-redirect sa screen ng pag-login.
Kapag ang application ay binuksan sa pamamagitan ng isang panlabas na link, kailangan munang i-load ang root screen, ibalik ang estado ng nabigasyon at pagkatapos lamang isagawa ang transition sa pamamagitan ng Deeplink. Ang direktang nabigasyon sa target na screen nang walang root context ay hahantong sa mga anomalya: walang laman na navigation stack o sirang back stack.
Sa pag-tap sa isang push notification, ang application ay maaaring nasa tatlong estado: sarado, nasa background o aktibo. Tinutukoy ng Deferred Navigation ang estado ng application, nilo-load ang kinakailangang nilalaman at pagkatapos lamang ipinapakita ang target na screen. Pinapayagan ng iOS na iproseso ang senaryong ito sa pamamagitan ng UNNotificationContentExtension.
Kung ang functionality ng screen ay pinamamahalaan ng isang feature flag mula sa server, pinapayagan ng deferred navigation na munang humiling ng configuration at pagkatapos lamang ipakita ang screen. Kung ang feature ay naka-disable, ang gumagamit ay nakakakita ng alternatibong nilalaman o placeholder sa halip na isang blangkong screen.
| Senaryo | Direktang nabigasyon | Deferred Navigation |
|---|---|---|
| Awtorisasyon | Blangkong screen sa nag-expire na token | Pag-redirect sa login |
| Deeplink | Sirang back stack | Tamang navigation stack |
| Push | Pag-load nang walang konteksto | Data handa bago ang transition |
| Feature flag | Pagpapakita ng hindi available na function | Placeholder o alternatibo |
Direktang nabigasyon (direct navigation) — tradisyonal na approach kung saan ang transition ay isinasagawa kaagad bilang tugon sa isang event. Pinindot ng gumagamit ang isang button, at agad na inilipat ng UI router ang screen. Ang approach na ito ay simple at predictable, ngunit limitado sa mga senaryo kung saan kailangan ang data mula sa server o pagsusuri ng mga kondisyon.
Deferred Navigation ay nagdaragdag ng isang layer sa anyo ng asynchronous na estado. Ang event ng gumagamit ay nagpapasimula ng isang operasyon, at ang subscription sa resulta ay namamahala ng nabigasyon. Pinapataas nito ang pagiging kumplikado ng code, ngunit nagbibigay ng flexibility: ang parehong trigger ay maaaring humantong sa iba't ibang mga screen depende sa na-load na data.
Ang pagpili sa pagitan ng dalawang approach ay depende sa mga kinakailangan: kung para sa pagpapakita ng screen ay hindi kinakailangan ang asynchronous na data — gamitin ang direktang nabigasyon. Kung ang screen ay depende sa resulta ng isang request, awtorisasyon o panlabas na kondisyon — sapilitan ang deferred navigation. Ang hybrid approach, kung saan ang bahagi ng mga transition ay direkta at ang bahagi ay naantala, ay ang pinakakaraniwang kasanayan sa mga pang-industriyang application.
Android Jetpack ay nagbibigay ng mga mekanismo para sa pagpapatupad ng Deferred Navigation sa antas ng arkitektura. Ang pangunahing ideya — ang ViewModel ay namamahala ng estado, at ang Activity o Fragment ay nag-subscribe sa mga pagbabago at nagpapatakbo ng nabigasyon sa pamamagitan ng NavController.
StateFlow sa Kotlin coroutines — perpektong tool para sa deferred navigation. Ina-update ng ViewModel ang StateFlow gamit ang navigation event, at ito ay inoobserbahan ng Activity at isinasagawa ang transition. Kapag na-proseso na ang event, ang StateFlow ay nililinis, pinipigilan ang paulit-ulit na nabigasyon.
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))
}
}
}
Sa Activity, ang subscription sa nabigasyon ay nagpapatakbo ng NavController na may ruta mula sa ViewModel. Para maiwasan ang paulit-ulit na nabigasyon sa pag-ikot ng screen, ginagamit ang NavigationEventWrapper na nagpoproseso ng event nang isang beses lamang. Ang Jetpack Navigation 2.7+ ay sumusuporta sa Safe Args para sa type-safe na pagpapadala ng argumento.
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 ay walang built-in na Navigation Component na katulad ng Android Jetpack, kaya ang mga developer ay nagpapatupad ng Deferred Navigation sa pamamagitan ng Coordinator Pattern kasama ng Combine o async/await. Ang Coordinator ay namamahala ng stack ng mga screen at gumagawa ng mga desisyon sa nabigasyon batay sa na-load na data.
Coordinator — isang bagay na namamahala ng nabigasyon sa pagitan ng mga ViewController. Kasama ng Combine, ang ViewModel ay naglalathala ng mga event sa pamamagitan ng PassthroughSubject, at ang Coordinator ay nag-subscribe sa mga ito at isinasagawa ang transition. Ang approach na ito ay ganap na naghihiwalay ng UI mula sa nabigasyong lohika at sumusunod sa mga rekomendasyon ng Apple para sa arkitektura ng application.
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)
}
}
Swift 5.5 ay nagpakilala ng structured concurrency, na nagpapahintulot sa pagpapatupad ng naantalang nabigasyon sa pamamagitan ng async/await nang walang Combine. Ang ViewModel ay nagbibigay ng isang async function na nagbabalik ng ruta pagkatapos mag-load ng data. Tinatawag ng Coordinator ang function na ito sa Task at isinasagawa ang transition ayon sa natanggap na ruta.
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)
}
}
// Sa Coordinator:
Task {
if let route = await viewModel.resolveDeeplink(url) {
navigateTo(route)
}
}
Deferred Navigation ay nagpapasimple ng pagproseso ng mga asynchronous na senaryo, ngunit nangangailangan ng disiplinadong approach sa pamamahala ng estado. Isaalang-alang natin ang mga pangunahing pagkakamali na ginagawa ng mga developer sa pagpapatupad ng deferred navigation.
Ang pinakakaraniwang pagkakamali — pagsubok na gawin ang deferred navigation bago ang root screen ay ganap na na-initialize at ang NavController o Coordinator ay handa na para sa transition. Sa Android ito ay humahantong sa IllegalStateException, sa iOS — sa hindi natukoy na estado ng UI. Solusyon — tiyakin na ang lifecycle ng component ay nasa estado na STARTED o RESUMED bago simulan ang nabigasyon.
Kung ang StateFlow o Subject ay hindi nililinis ang event pagkatapos ng pagproseso, sa pagbalik sa nakaraang screen ang gumagamit ay maaaring awtomatikong ma-redirect sa parehong screen muli. Gumamit ng SharedFlow na may replay=0 sa Android o CurrentValueSubject na may nil pagkatapos ng pagproseso sa iOS, upang ang navigation event ay ma-trigger nang isang beses lamang.
Gumamit ng isang sentral na component para sa buong nabigasyon sa application. Kapag ang bawat Activity, Fragment o ViewController ay may sariling navigation controller, ang deferred navigation sa pagitan ng iba't ibang bahagi ng application ay nagiging magulo. Ang isang Coordinator ay nagpapadali sa debugging at pagsubok ng mga navigation scenario.
Ang Deferred Navigation ay mas mahirap subukan kaysa sa direktang nabigasyon dahil ang mga asynchronous na operasyon ay nagpapakilala ng factor ng oras. Gumamit ng TestDispatcher sa Android (kotlinx-coroutines-test) at XCTestExpectation sa iOS para sa simulation ng pag-load ng data at suriin na ang nabigasyon ay isinasagawa ayon sa inaasahang ruta. I-mock ang mga serbisyo ng awtorisasyon at deeplink para sa isolated na pagsubok ng bawat senaryo.
Mga madalas itanong
Deferred Navigation ay isang pattern ng naantalang transition na maaaring ilapat sa anumang asynchronous na senaryo. Ang Deep Link ay isa sa mga trigger para sa deferred navigation, ngunit hindi lamang ito. Ang awtorisasyon at feature flag ay gumagamit din ng naantalang nabigasyon.
Oo, karamihan sa mga application ay gumagamit ng hybrid approach. Ang screen ng listahan ng produkto (walang asynchronous dependencies) ay maaaring gumamit ng direktang nabigasyon, at ang screen ng detalye na may pag-load ng data — deferred. Ang paghahati ay tinutukoy ng arkitektura ng partikular na screen.
Gumamit ng SharedFlow na walang replay sa Android at combineLatest na walang buffering sa iOS. Kanselahin ang mga nakaraang subscription sa bagong trigger. Ito ay ginagarantiyahan na ang huling navigation event lamang ang pinoproseso.
Oo, sa Compose ang deferred navigation ay ipinapatupad sa pamamagitan ng subscription sa StateFlow ng ViewModel at pagtawag ng NavController.navigate sa LaunchedEffect. Inirerekomenda ng Google ang paggamit ng Navigation Compose na may event model para sa mga deferred scenario.
Ipagpaliban ang nabigasyon hanggang sa maging aktibo muli ang application. Sa Android gamitin ang Lifecycle.State.STARTED para sa pag-filter ng mga event. Sa iOS suriin ang UIApplication.State sa Combine o async/await block.
Buod
Gagawa kami ng mobile application na turnkey
Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.
Basahin din