val navController = view.findNavController() // 这个view必须在一个NavHost之中
activity
先获取Activity中的NavHostFragment,再获取NavController。
1 2 3
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment val navController = navHostFragment.navController
@JvmStatic publicfunfindNavController(fragment: Fragment): NavController { var findFragment: Fragment? = fragment while (findFragment != null) { if (findFragment is NavHostFragment) { return findFragment.navHostController } val primaryNavFragment = findFragment.parentFragmentManager .primaryNavigationFragment if (primaryNavFragment is NavHostFragment) { return primaryNavFragment.navHostController } findFragment = findFragment.parentFragment }
// Try looking for one associated with the view instead, if applicable val view = fragment.view if (view != null) { return Navigation.findNavController(view) }
// For DialogFragments, look at the dialog's decor view val dialogDecorView = (fragment as? DialogFragment)?.dialog?.window?.decorView if (dialogDecorView != null) { return Navigation.findNavController(dialogDecorView) } throw IllegalStateException("Fragment $fragment does not have a NavController set") }
publicfun Activity.findNavController( @IdRes viewId: Int ): NavController = Navigation.findNavController(this, viewId)
1 2 3 4 5 6 7 8
@JvmStatic publicfunfindNavController(activity: Activity, @IdRes viewId: Int): NavController { val view = ActivityCompat.requireViewById<View>(activity, viewId) return findViewNavController(view) ?: throw IllegalStateException( "Activity $activity does not have a NavController set on $viewId" ) }
@Suppress("UNCHECKED_CAST") privatefungetViewNavController(view: View): NavController? { val tag = view.getTag(R.id.nav_controller_view_tag) var controller: NavController? = null if (tag is WeakReference<*>) { controller = (tag as WeakReference<NavController>).get() } elseif (tag is NavController) { controller = tag } return controller }
<navigationxmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/nav_graph" app:startDestination="@id/profile"> <!-- Action back to destination which launched into this profile --> <actionandroid:id="@+id/action_global_profile" app:popUpTo="@id/profile" app:popUpToInclusive="true" /> <fragment android:id="@+id/profile" android:name="com.example.ProfileFragment" android:label="Profile">
<!-- Action to navigate from Profile to Friends List. --> <action android:id="@+id/action_profile_to_friendslist" app:destination="@id/friendslist" /> </fragment>
/** * Callback for when the [NavController][getNavController] is created. If you * support any custom destination types, their [Navigator] should be added here to * ensure it is available before the navigation graph is inflated / set. * * By default, this adds a [DialogFragmentNavigator] and [FragmentNavigator]. * * This is only called once when the navController is called. This will be called in [onCreate] * if the navController has not yet been called. This should not be called directly by * subclasses. * * @param navController The newly created [NavController]. */ @Suppress("DEPRECATION") @CallSuper @Deprecated( """Override {@link #onCreateNavHostController(NavHostController)} to gain access to the full {@link NavHostController} that is created by this NavHostFragment.""" ) protectedopenfunonCreateNavController(navController: NavController) { navController.navigatorProvider += DialogFragmentNavigator(requireContext(), childFragmentManager) navController.navigatorProvider.addNavigator(createFragmentNavigator()) }
@CallSuper publicoverridefunonCreate(savedInstanceState: Bundle?) { // We are accessing the NavController here to ensure that it is always created by this point navHostController if (savedInstanceState != null) { if (savedInstanceState.getBoolean(KEY_DEFAULT_NAV_HOST, false)) { defaultNavHost = true parentFragmentManager.beginTransaction() .setPrimaryNavigationFragment(this) .commit() } }
// We purposefully run this last as this will trigger the onCreate() of // child fragments, which may be relying on having the NavController already // created and having its state restored by that point. super.onCreate(savedInstanceState) }
publicvoidsetArguments(@Nullable Bundle args) { if (mFragmentManager != null && isStateSaved()) { thrownewIllegalStateException("Fragment already added and state has been saved"); } mArguments = args; }
@CallSuper publicoverridefunonCreate(savedInstanceState: Bundle?) { // We are accessing the NavController here to ensure that it is always created by this point navHostController if (savedInstanceState != null) { if (savedInstanceState.getBoolean(KEY_DEFAULT_NAV_HOST, false)) { defaultNavHost = true parentFragmentManager.beginTransaction() .setPrimaryNavigationFragment(this) .commit() } }
// We purposefully run this last as this will trigger the onCreate() of // child fragments, which may be relying on having the NavController already // created and having its state restored by that point. super.onCreate(savedInstanceState) }
onViewCreated
会给view设置navHostController
1 2 3 4 5 6 7 8 9 10 11 12 13
publicoverridefunonViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) check(view is ViewGroup) { "created host view $view is not a ViewGroup" } Navigation.setViewNavController(view, navHostController) // When added programmatically, we need to set the NavController on the parent - i.e., // the View that has the ID matching this NavHostFragment. if (view.getParent() != null) { viewParent = view.getParent() as View if (viewParent!!.id == id) { Navigation.setViewNavController(viewParent!!, navHostController) } } }
voidperformPrimaryNavigationFragmentChanged() { booleanisPrimaryNavigationFragment= mFragmentManager.isPrimaryNavigation(this); // Only send out the callback / dispatch if the state has changed if (mIsPrimaryNavigationFragment == null || mIsPrimaryNavigationFragment != isPrimaryNavigationFragment) { mIsPrimaryNavigationFragment = isPrimaryNavigationFragment; onPrimaryNavigationFragmentChanged(isPrimaryNavigationFragment); mChildFragmentManager.dispatchPrimaryNavigationFragmentChanged(); } }
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) publicopenfunsetViewModelStore(viewModelStore: ViewModelStore) { if (viewModel == NavControllerViewModel.getInstance(viewModelStore)) { return } check(backQueue.isEmpty()) { "ViewModelStore should be set before setGraph call" } viewModel = NavControllerViewModel.getInstance(viewModelStore) }
/** * Sets the [navigation graph][NavGraph] to the specified graph. * Any current navigation graph data (including back stack) will be replaced. * * The graph can be retrieved later via [graph]. * * @param graph graph to set * @see NavController.setGraph * @see NavController.graph */ @MainThread @CallSuper publicopenfunsetGraph(graph: NavGraph, startDestinationArgs: Bundle?) { if (_graph != graph) { _graph?.let { previousGraph -> // Clear all saved back stacks by iterating through a copy of the saved keys, // thus avoiding any concurrent modification exceptions val savedBackStackIds = ArrayList(backStackMap.keys) savedBackStackIds.forEach { id -> clearBackStackInternal(id) } // Pop everything from the old graph off the back stack NavControllerInternal(previousGraph.id, true) } _graph = graph onGraphCreated(startDestinationArgs) } else { // first we update _graph with new instances from graph for (i in0 until graph.nodes.size()) { val newDestination = graph.nodes.valueAt(i) val key = _graph!!.nodes.keyAt(i) _graph!!.nodes.replace(key, newDestination) } // then we update backstack with the new instances backQueue.forEach { entry -> // we will trace this hierarchy in new graph to get new destination instance val hierarchy = entry.destination.hierarchy.toList().asReversed() val newDestination = hierarchy.fold(_graph!!) { newDest: NavDestination, oldDest: NavDestination -> if (oldDest == _graph && newDest == graph) { // if root graph, it is already the node that matches with oldDest newDest } elseif (newDest is NavGraph) { // otherwise we walk down the hierarchy to the next child newDest.findNode(oldDest.id)!! } else { // final leaf node found newDest } } entry.destination = newDestination } } }
考虑了两种情况,setGraph前后_graph和graph是否相等
注意kt代码有运算符重载,实际上调用的是NavGraph的equals
1 2 3 4 5 6 7 8
overridefunequals(other: Any?): Boolean { if (this === other) returntrue if (other == null || other !is NavGraph) returnfalse returnsuper.equals(other) && nodes.size == other.nodes.size && startDestinationId == other.startDestinationId && nodes.valueIterator().asSequence().all { it == other.nodes.get(it.id) } }
privatefunrestoreStateInternal( id: Int, args: Bundle?, navOptions: NavOptions?, navigatorExtras: Navigator.Extras? ): Boolean { if (!backStackMap.containsKey(id)) { returnfalse// 1: 首先是防止backStackMap中没有对应的node } val backStackId = backStackMap[id] // Clear out the state we're going to restore so that it isn't restored a second time backStackMap.values.removeAll { it == backStackId } // 2: 删除map中所有和backStackId相等的entry val backStackState = backStackStates.remove(backStackId) // 3: 删除backStackStates中对应的entry // Now restore the back stack from its saved state val entries = instantiateBackStack(backStackState) // 4: 更新backStackState中的NavBackStackEntryState return executeRestoreState(entries, args, navOptions, navigatorExtras) // 5: 找到Navigator,导航到对应位置 }
privatefunexecuteRestoreState( entries: List<NavBackStackEntry>, args: Bundle?, navOptions: NavOptions?, navigatorExtras: Navigator.Extras? ): Boolean { // Split up the entries by Navigator so we can restore them as an atomic operation val entriesGroupedByNavigator = mutableListOf<MutableList<NavBackStackEntry>>() entries.filterNot { entry -> // Skip navigation graphs - they'll be added by addEntryToBackStack() entry.destination is NavGraph }.forEach { entry -> val previousEntryList = entriesGroupedByNavigator.lastOrNull() val previousNavigatorName = previousEntryList?.last()?.destination?.navigatorName if (previousNavigatorName == entry.destination.navigatorName) { // Group back to back entries associated with the same Navigator together previousEntryList += entry } else { // Create a new group for the new Navigator entriesGroupedByNavigator += mutableListOf(entry) } // 1: 如果 navigatorName和前一个的相等,则添加到前一个的list中,否则添加到新建一个list中,最后会得到一个二维数组,每个数组中的元素都是具有相同NavigatorName的 } var navigated = false // Now actually navigate to each set of entries for (entryList in entriesGroupedByNavigator) { val navigator = _navigatorProvider.getNavigator<Navigator<NavDestination>>( entryList.first().destination.navigatorName ) // 2: 每个entryList都具有相同的navigatorName,找到对应的Navigator var lastNavigatedIndex = 0 navigator.navigateInternal(entryList, navOptions, navigatorExtras) { entry -> navigated = true // If this destination is part of the restored back stack, // pass all destinations between the last navigated entry and this one // to ensure that any navigation graphs are properly restored as well val entryIndex = entries.indexOf(entry) val restoredEntries = if (entryIndex != -1) { entries.subList(lastNavigatedIndex, entryIndex + 1).also { lastNavigatedIndex = entryIndex + 1 } } else { emptyList() } addEntryToBackStack(entry.destination, args, entry, restoredEntries) } // 执行导航操作 } return navigated }
overridefunpush(backStackEntry: NavBackStackEntry) { val destinationNavigator: Navigator<out NavDestination> = _navigatorProvider[backStackEntry.destination.navigatorName] if (destinationNavigator == navigator) { val handler = addToBackStackHandler // 还记得前面存起来的回调吗,就会在这里调用 if (handler != null) { handler(backStackEntry) addInternal(backStackEntry) } else { // TODO handle the Navigator calling add() outside of a call to navigate() Log.i( TAG, "Ignoring add of destination ${backStackEntry.destination} " + "outside of the call to navigate(). " ) } } else { val navigatorBackStack = checkNotNull(navigatorState[destinationNavigator]) { "NavigatorBackStack for ${backStackEntry.destination.navigatorName} should " + "already be created" } navigatorBackStack.push(backStackEntry) } }
privatefunlaunchSingleTopInternal( node: NavDestination, args: Bundle? ): Boolean { val currentBackStackEntry = currentBackStackEntry val nodeId = if (node is NavGraph) node.findStartDestination().id else node.id if (nodeId != currentBackStackEntry?.destination?.id) returnfalse
val tempBackQueue: ArrayDeque<NavBackStackEntry> = ArrayDeque() // pop from startDestination back to original node and create a new entry for each backQueue.indexOfLast { it.destination === node }.let { nodeIndex -> // 找到queue中最后一个destination和node相等的index while (backQueue.lastIndex >= nodeIndex) { // 如果queue中最后一个index大于nodeIndex,就删掉他,并创建一个新的entry,放到tempBackQueue中,直到lastIndex小于nodeIndex为止 val oldEntry = backQueue.removeLast() unlinkChildFromParent(oldEntry) // 删除父子关系 val newEntry = NavBackStackEntry( oldEntry, oldEntry.destination.addInDefaultArgs(args) ) // 创建新的entry tempBackQueue.addFirst(newEntry) // 添加到tempBackQueue中 } }
// add each new entry to backQueue starting from original node to startDestination tempBackQueue.forEach { newEntry -> val parent = newEntry.destination.parent if (parent != null) { val newParent = getBackStackEntry(parent.id) linkChildToParent(newEntry, newParent) // 更新entry之间的父子关系 } backQueue.add(newEntry) }
// we replace NavState entries here only after backQueue has been finalized tempBackQueue.forEach { newEntry -> val navigator = _navigatorProvider.getNavigator<Navigator<*>>( newEntry.destination.navigatorName ) navigator.onLaunchSingleTop(newEntry) // 拿到对应的Navigator,执行onLaunchSingleTop }
publicopenfunonLaunchSingleTop(backStackEntry: NavBackStackEntry) { // We update the back stack here because we don't want to leave it to the navigator since // it might be using transitions. backStackLock.withLock { val tempStack = backStack.value.toMutableList() tempStack.indexOfLast { it.id == backStackEntry.id }.let { idx -> tempStack[idx] = backStackEntry } _backStack.value = tempStack } }
if (!launchSingleTop) { // Not a single top operation, so we're looking to add the node to the back stack val backStackEntry = NavBackStackEntry.create( context, node, finalArgs, hostLifecycleState, viewModel ) val navigator = _navigatorProvider.getNavigator<Navigator<NavDestination>>( node.navigatorName ) navigator.navigateInternal(listOf(backStackEntry), navOptions, navigatorExtras) { navigated = true addEntryToBackStack(node, finalArgs, it) } }
publicfun<T : Navigator<*>> getNavigator(navigatorClass: Class<T>): T { val name = getNameForNavigator(navigatorClass) return getNavigator(name) }
/** * Retrieves a registered [Navigator] by name. * * @param name name of the navigator to return * @return the registered navigator with the given name * * @throws IllegalStateException if the Navigator has not been added * * @see NavigatorProvider.addNavigator */ @Suppress("UNCHECKED_CAST") @CallSuper publicopenfun<T : Navigator<*>> getNavigator(name: String): T { require(validateName(name)) { "navigator name cannot be an empty string" } val navigator = _navigators[name] ?: throw IllegalStateException( "Could not find Navigator with name \"$name\". You must call " + "NavController.addNavigator() for each navigation type." ) return navigator as T }
@JvmStatic internalfungetNameForNavigator(navigatorClass: Class<outNavigator<*>>): String { var name = annotationNames[navigatorClass] if (name == null) { valannotation = navigatorClass.getAnnotation( Navigator.Name::class.java ) name = annotation?.value require(validateName(name)) { "No @Navigator.Name annotation found for ${navigatorClass.simpleName}" } annotationNames[navigatorClass] = name } return name!! } }
/** * Register a navigator by name. [destinations][NavDestination] may refer to any * registered navigator by name for inflation. If a navigator by this name is already * registered, this new navigator will replace it. * * @param name name for this navigator * @param navigator navigator to add * @return the previously added Navigator for the given name, if any */ @CallSuper publicopenfunaddNavigator( name: String, navigator: Navigator<outNavDestination> ): Navigator<out NavDestination>? { require(validateName(name)) { "navigator name cannot be an empty string" } val previousNavigator = _navigators[name] if (previousNavigator == navigator) { return navigator } check(previousNavigator?.isAttached != true) { "Navigator $navigator is replacing an already attached $previousNavigator" } check(!navigator.isAttached) { "Navigator $navigator is already attached to another NavController" } return _navigators.put(name, navigator) }
Representation of an entry in the back stack of a androidx.navigation.NavController. The Lifecycle, ViewModelStore, and SavedStateRegistry provided via thisobject are valid for the lifetime of this destination on the back stack: whenthis destination is popped off the back stack, the lifecycle will be destroyed, state will no longer be saved, and ViewModels will be cleared.
介绍了两件事情
这个类代表的是NavController中返回栈的一个entry
他能提供Lifecycle, ViewModelStore, and SavedStateRegistry
publicclassNavBackStackEntryprivateconstructor( privateval context: Context?, /** * The destination associated with this entry * @return The destination that is currently visible to users */ @set:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) publicvar destination: NavDestination, privateval immutableArgs: Bundle? = null, privatevar hostLifecycleState: Lifecycle.State = Lifecycle.State.CREATED, privateval viewModelStoreProvider: NavViewModelStoreProvider? = null, /** * The unique ID that serves as the identity of this entry * @return the unique ID of this entry */ publicval id: String = UUID.randomUUID().toString(), privateval savedState: Bundle? = null ) : LifecycleOwner, ViewModelStoreOwner, HasDefaultViewModelProviderFactory, SavedStateRegistryOwner { privatevar _lifecycle = LifecycleRegistry(this)
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) publicfunupdateState() { if (!savedStateRegistryAttached) { savedStateRegistryController.performAttach() savedStateRegistryAttached = true if (viewModelStoreProvider != null) { enableSavedStateHandles() } // Perform the restore just once, the first time updateState() is called // and specifically *before* we move up the Lifecycle savedStateRegistryController.performRestore(savedState) } if (hostLifecycleState.ordinal < maxLifecycle.ordinal) { _lifecycle.currentState = hostLifecycleState } else { _lifecycle.currentState = maxLifecycle } }
publicclassNavBackStackEntryprivateconstructor( privateval context: Context?, /** * The destination associated with this entry * @return The destination that is currently visible to users */ @set:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) publicvar destination: NavDestination, privateval immutableArgs: Bundle? = null, privatevar hostLifecycleState: Lifecycle.State = Lifecycle.State.CREATED, privateval viewModelStoreProvider: NavViewModelStoreProvider? = null, /** * The unique ID that serves as the identity of this entry * @return the unique ID of this entry */ publicval id: String = UUID.randomUUID().toString(), privateval savedState: Bundle? = null ) : LifecycleOwner, ViewModelStoreOwner, HasDefaultViewModelProviderFactory, SavedStateRegistryOwner { privateval defaultFactory by lazy { SavedStateViewModelFactory((context?.applicationContext as? Application), this, arguments) }
/** * The [SavedStateHandle] for this entry. */ publicval savedStateHandle: SavedStateHandle by lazy { check(savedStateRegistryAttached) { "You cannot access the NavBackStackEntry's SavedStateHandle until it is added to " + "the NavController's back stack (i.e., the Lifecycle of the NavBackStackEntry " + "reaches the CREATED state)." } check(lifecycle.currentState != Lifecycle.State.DESTROYED) { "You cannot access the NavBackStackEntry's SavedStateHandle after the " + "NavBackStackEntry is destroyed." } ViewModelProvider( this, NavResultSavedStateFactory(this) ).get(SavedStateViewModel::class.java).handle } overrideval defaultViewModelProviderFactory: ViewModelProvider.Factory = defaultFactory
publicclassNavBackStackEntryprivateconstructor( privateval context: Context?, /** * The destination associated with this entry * @return The destination that is currently visible to users */ @set:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) publicvar destination: NavDestination, privateval immutableArgs: Bundle? = null, privatevar hostLifecycleState: Lifecycle.State = Lifecycle.State.CREATED, privateval viewModelStoreProvider: NavViewModelStoreProvider? = null, /** * The unique ID that serves as the identity of this entry * @return the unique ID of this entry */ publicval id: String = UUID.randomUUID().toString(), privateval savedState: Bundle? = null ) : LifecycleOwner, ViewModelStoreOwner, HasDefaultViewModelProviderFactory, SavedStateRegistryOwner { privateval savedStateRegistryController = SavedStateRegistryController.create(this) privatevar savedStateRegistryAttached = false
/** * The [SavedStateHandle] for this entry. */ publicval savedStateHandle: SavedStateHandle by lazy { check(savedStateRegistryAttached) { "You cannot access the NavBackStackEntry's SavedStateHandle until it is added to " + "the NavController's back stack (i.e., the Lifecycle of the NavBackStackEntry " + "reaches the CREATED state)." } check(lifecycle.currentState != Lifecycle.State.DESTROYED) { "You cannot access the NavBackStackEntry's SavedStateHandle after the " + "NavBackStackEntry is destroyed." } ViewModelProvider( this, NavResultSavedStateFactory(this) ).get(SavedStateViewModel::class.java).handle }
publicclassNavBackStackEntryprivateconstructor( privateval context: Context?, /** * The destination associated with this entry * @return The destination that is currently visible to users */ @set:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) publicvar destination: NavDestination, privateval immutableArgs: Bundle? = null, privatevar hostLifecycleState: Lifecycle.State = Lifecycle.State.CREATED, privateval viewModelStoreProvider: NavViewModelStoreProvider? = null, /** * The unique ID that serves as the identity of this entry * @return the unique ID of this entry */ publicval id: String = UUID.randomUUID().toString(), privateval savedState: Bundle? = null ) : LifecycleOwner, ViewModelStoreOwner, HasDefaultViewModelProviderFactory, SavedStateRegistryOwner { publicval arguments: Bundle? get() = if (immutableArgs == null) { null } else { Bundle(immutableArgs) } }
NavDestination represents one node within an overall navigation graph. Each destination is associated with a Navigator which knows how to navigate to this particular destination. Destinations declare a set of actions that they support. These actions form a navigation API for the destination; the same actions declared on different destinations that fill similar roles allow application code to navigate based on semantic intent. Each destination has a set of arguments that will be applied when navigating to that destination. Any default values for those arguments can be overridden at the time of navigation. NavDestinations should be created via Navigator.createDestination.
This optional annotation allows tooling to offer auto-complete for the android:name attribute. This should match the classtypepassedtoparseClassFromNamewhenparsingtheandroid:nameattribute.
publicfunaddDeepLink(navDeepLink: NavDeepLink) { val missingRequiredArguments = _arguments.missingRequiredArguments { key -> key !in navDeepLink.argumentsNames } require(missingRequiredArguments.isEmpty()) { "Deep link ${navDeepLink.uriPattern} can't be used to open destination $this.\n" + "Following required arguments are missing: $missingRequiredArguments" }
NavGraph is a collection of NavDestination nodes fetchable by ID. A NavGraph serves as a 'virtual' destination: while the NavGraph itself will not appear on the back stack, navigating to the NavGraph will cause the starting destination to be added to the back stack. Construct a new NavGraph. This NavGraph is not valid until you add a destination and set the starting destination. Params: navGraphNavigator - The NavGraphNavigator which this destination will be associated with. Generally retrieved via a NavController'sNavigatorProvider.getNavigator method.
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) publicoverridefunmatchDeepLink(navDeepLinkRequest: NavDeepLinkRequest): DeepLinkMatch? { // First search through any deep links directly added to this NavGraph val bestMatch = super.matchDeepLink(navDeepLinkRequest) // Then search through all child destinations for a matching deep link val bestChildMatch = mapNotNull { child -> child.matchDeepLink(navDeepLinkRequest) }.maxOrNull()