preface
I first got to know this component at Google IO in ’18, and it was interesting to see them showing visual navigation diagrams, but I didn’t really understand what it was for
I didn’t understand its wonderful use until I used this component in the new project I came into contact with recently. In order to better use and expand the function of Navigation, the author analyzes its implementation principle here
Navigation
For instructions on Navigation, please refer to the Android Developer documentation. Google also provides a sample to learn
role
Once experienced, Navigation can help us solve the following problems
- Redirect the Activity/Fragment using the ID
- Reduces the coupling of classes and only cares about ids
- On this basis, the functions of routing components can also be extended, and the registration process is the combination of navigation charts
- It helps us manage the Fragment jump animation and unstack, and lets us use the Fragment as an Activity
- Provide good technical support for single Activity and multi-fragment app
About single Activity and multiple fragments
Have you ever heard of a single Activity with multiple fragments? Instagram, Twitter and other popular apps have adopted this approach? What are the benefits? After in-depth understanding of the Android system architecture, THE author has his own views on this question
- When attaching, the Window will be created and a ViewRootImpl will be created. There is a Surface in the ViewRootImpl. After the Surface is initialized, The SurfaceFlinger process corresponds to one Layer, that is, the application with multiple activities will have multiple layers. When VSYNC is emitted, SF will be responsible for Layer sorting and compositing. When there are too many layers, SF Compose will increase the burden of SF Compose to some extent. Therefore, when the Layer is small, the smoothness of our App can be increased to a certain extent
- In addition, every Activity needs to communicate with SystemService across processes when it is started, but Fragment does not. In theory, it can reduce the time of page hopping
We know that the first problem we face when using fragments like activities is managing their backlogs, and the Navigation component helps us with this
So let’s go back to a navigation and onBackPressed and see how does the navigation control the Fragment start and exit
Initialization
<fragment
android:id="@+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/mobile_navigation" />
Copy the code
To use Navigation, add a tag to the layout of the main Activity that specifies that the current Activity has a NavHostFragment inside it and its Navigation scope is mobile_navigation. How is NavHostFragment initialized
A. The onCreate
public class NavHostFragment extends Fragment implements NavHost {
private NavController mNavController;
@NonNull
@Override
public NavController getNavController() {
if (mNavController == null) {
throw new IllegalStateException("NavController is not available before onCreate()");
}
returnmNavController; } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); final Context context = requireContext(); MNavController = new NavHostController(context); / / 2. The registered lifecycle callback mNavController. SetLifecycleOwner (this); / / 3. Registered return event dispenser mNavController. SetOnBackPressedDispatcher (requireActivity () getOnBackPressedDispatcher ()); // 4. Create a Fragment navigator and add it to NavController to manage onCreateNavController(mNavController); .if(navState ! = null) { // Navigation controller state overrides arguments mNavController.restoreState(navState); }else{ final Bundle args = getArguments(); ResId Final int graphId = args! = null ? args.getInt(KEY_GRAPH_ID) : 0;if(graphId ! = 0) { mNavController.setGraph(graphId); }else{ mNavController.setMetadataGraph(); }}} protected void onCreateNavController(@nonnull NavController NavController) {// Add DialogFragment and the Fragment navigator navController.getNavigatorProvider().addNavigator( new DialogFragmentNavigator(requireContext(), getChildFragmentManager())); navController.getNavigatorProvider().addNavigator(createFragmentNavigator()); }}Copy the code
As can be seen from the inheritance relationship of NavHostFragment, it implements NavHost interface, that is to say, it holds NavController internally. Its onCreate method is mainly used to build and initialize NavController
- Create navigation controller NavHostController, which is a wrapper class for NavController
- Register life cycle callbacks
- Register return event dispatcher
- Callback onCreateNavController to inject the Fragment/DialogFragment navigator into NavController
- Inject a navigation diagram for the NavController
We will focus on the instantiation of the NavController first, but we will discuss the implementation of the navigator function and the distribution of return events later
Instantiation of NavController
Public class NavController {// Final Deque<NavDestination> mBackStack = new ArrayDeque<>(); Private Final SimpleNavigatorProvider mNavigatorProvider = newSimpleNavigatorProvider() {
@Nullable
@Override
public Navigator<? extends NavDestination> addNavigator(@NonNull String name,
@NonNull Navigator<? extends NavDestination> navigator) {
Navigator<? extends NavDestination> previousNavigator =
super.addNavigator(name, navigator);
if(previousNavigator ! = navigator) {// Clear the listener for navigatorif(previousNavigator ! = null) { previousNavigator.removeOnNavigatorNavigatedListener(mOnNavigatedListener); } / / add new Navigator listeners Navigator. AddOnNavigatorNavigatedListener (mOnNavigatedListener); }returnpreviousNavigator; }}; public NavController(@NonNull Context context) { mContext = context;while (context instanceof ContextWrapper) {
if (context instanceof Activity) {
mActivity = (Activity) context;
break; } context = ((ContextWrapper) context).getBaseContext(); } / / add a NavGraphNavigator mNavigatorProvider. AddNavigator (new NavGraphNavigator (mContext)); / / add a ActivityNavigator mNavigatorProvider. AddNavigator (new ActivityNavigator (mContext)); }}Copy the code
The NavController is named the navigation controller, as it is, and internally holds a NavigatorProvider for caching and providing the navigator
As you can see in its constructor, it adds two navigators, NavGraphNavigator and ActivityNavigator, respectively. In addition to FragmentNavigator, there are three navigators. They are responsible for the implementation of navigation for specific business scenarios, which we will analyze as we use them
2) review
Here you can see the structure design of its Navigation is more clear
- NavController: Provides interfaces for the upper layer, navigation services and stack maintenance
- NavigationProvider: Maintains the navigator
- Navigator: a Navigator that handles specific jumps and exits
- ActivityNavigator
- FragmentNavigator
- GraphNavigator
Now that you understand the design structure, look at the specific navigate operation
Two. Navigation operation
public class NavController { public void navigate(@IdRes int resId, @Nullable Bundle args, @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) { // 1. NavDestination currentNode = mbackStack.isEmpty ()? mGraph : mBackStack.getLast().getDestination();if (currentNode == null) {
throw new IllegalStateException("no current navigation node"); } @idres int destId = resId; final NavAction navAction = currentNode.getAction(resId); Bundle combinedArgs = null;if(navAction ! = null) {// 2.1 Reads navigation configuration options from NavActionif(navOptions == null) { navOptions = navAction.getNavOptions(); } / / 2.2 for destination ID destId = navAction getDestinationId (); / / 2.3 into the general parameters of the Bundle navActionArgs = navAction. GetDefaultArguments ();if (navActionArgs != null) {
combinedArgs = new Bundle();
combinedArgs.putAll(navActionArgs);
}
}
if(args ! = null) {if(combinedArgs == null) { combinedArgs = new Bundle(); } combinedArgs.putAll(args); } // 3. If only the ID of popUp exists, the stack operation is performedif(destId == 0 && navOptions ! = null && navOptions.getPopUpTo() ! = 1) {/ / perform a stack logic popBackStack (navOptions. GetPopUpTo (), navOptions. IsPopUpToInclusive ());return;
}
if (destId == 0) {
throw new IllegalArgumentException("Destination id == 0 can only be used"
+ " in conjunction with a valid navOptions.popUpTo"); NavDestination node = findDestination(destId);if (node == null) {
final String dest = NavDestination.getDisplayName(mContext, destId);
throw new IllegalArgumentException("navigation destination "+ dest + (navAction ! = null ?" referenced from action " + NavDestination.getDisplayName(mContext, resId)
: "")
+ " is unknown to this NavController"); } // navigate(node, combinedArgs, navOptions, navigatorExtras); }}Copy the code
The details of a navigation process are as follows
- Gets the element at the top of the stack
- Get the NavAction object corresponding to the ID, describing the navigation action
- NavAction is the tag element information that we define in the XML of the navigation diagram
- If only the PopUpId exists, the popBackStack is called to remove the stack
- If destId is present, the overloaded navigate method is called to perform the push operation
Now let’s look at the push operation
A) into the stack
public class NavController { private void navigate(@NonNull NavDestination node, @Nullable Bundle args, @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) { ...... / / 1. According to the Name of the node to obtain the corresponding Navigator Navigator < NavDestination > the Navigator. = mNavigatorProvider getNavigator ( node.getNavigatorName()); Bundle finalArgs = node.addInDefaultArgs(args); Navigator. navigate(node, finalArgs, navOptions, navigatorExtras); // 3. Navigation succeeded, update stack elementif(newDest ! = null) { ...... // 3.1 Add an element of type mGraph to the fallback stack, keeping it at the bottomif(mBackStack.isEmpty()) { mBackStack.add(new NavBackStackEntry(mGraph, finalArgs, mViewModel)); ArrayDeque<NavBackStackEntry> Hierarchy = new ArrayDeque<>(); NavDestination destination = newDest;while(destination ! = null && findDestination(destination.getId()) == null) { NavGraph parent = destination.getParent();if(parent ! = null) { hierarchy.addFirst(new NavBackStackEntry(parent, finalArgs, mViewModel)); } destination = parent; } mBackStack.addAll(hierarchy); NavBackStackEntry newBackStackEntry = new NavBackStackEntry(newDest, newDest.addInDefaultArgs(finalArgs), mViewModel); mBackStack.add(newBackStackEntry); } updateOnBackPressedCallbackEnabled();if(popped || newDest ! = null) { dispatchOnDestinationChanged(); }}}Copy the code
The operation of pushing the stack is relatively clear, mainly consisting of the following three steps
- Get the corresponding Navigator based on the destination’s information
- Call navigator. navigate to the appropriate page
- If newDest exists, the navigation operation is of the Fragment type and is added to the mBackStack for maintenance
The ActivityNavigator implementation class returns null after navigation. Since the Activity stack is maintained by AMS, we really don’t need to manually maintain it again, so here we select FragmentNavigator to see its implementation
FragmentNavigator push operation
public class FragmentNavigator extends Navigator<FragmentNavigator.Destination> { private final Context mContext; private final FragmentManager mFragmentManager; private final int mContainerId; private ArrayDeque<Integer> mBackStack = new ArrayDeque<>(); public FragmentNavigator(@NonNull Context context, @NonNull FragmentManager manager, int containerId) { mContext = context; mFragmentManager = manager; // Id of the container mContainerId = containerId; } @Override public void navigate(@NonNull Destination destination, @Nullable Bundle args, @ Nullable NavOptions NavOptions) {/ / to create fragments instance object final fragments frag = destination. CreateFragment (args); / / build the Transaction final FragmentTransaction ft. = mFragmentManager beginTransaction (); Int enterAnim = navOptions! = null ? navOptions.getEnterAnim() : -1; intexitAnim = navOptions ! = null ? navOptions.getExitAnim() : -1; int popEnterAnim = navOptions ! = null ? navOptions.getPopEnterAnim() : -1; int popExitAnim = navOptions ! = null ? navOptions.getPopExitAnim() : -1;if(enterAnim ! = 1 | |exitAnim ! = -1 || popEnterAnim ! = -1 || popExitAnim ! = -1) { enterAnim = enterAnim ! = 1? enterAnim : 0;exitAnim = exitAnim ! = 1?exitAnim : 0; popEnterAnim = popEnterAnim ! = 1? popEnterAnim : 0; popExitAnim = popExitAnim ! = 1? popExitAnim : 0; ft.setCustomAnimations(enterAnim,exitAnim, popEnterAnim, popExitAnim); } ft.replace(mContainerId, frag); final @IdRes int destId = destination.getId(); final boolean initialNavigation = mBackStack.isEmpty(); final boolean isClearTask = navOptions ! = null && navOptions.shouldClearTask(); Final Boolean isSingleTopReplacement = navOptions! = null && ! initialNavigation && navOptions.shouldLaunchSingleTop() && mBackStack.peekLast() == destId; int backStackEffect;if (initialNavigation || isClearTask) {
backStackEffect = BACK_STACK_DESTINATION_ADDED;
} else if(isSingleTopReplacement) {// Handle SingleTopif (mBackStack.size() > 1) {
// If the Fragment to be replaced is on the FragmentManager's // back stack, a simple replace() isn't enough so we
// remove it from the back stack and put our replacement
// on the back stack in its place
mFragmentManager.popBackStack();
ft.addToBackStack(getBackStackName(destId));
mPendingBackStackOperations++;
}
backStackEffect = BACK_STACK_UNCHANGED;
} else {
ft.addToBackStack(getBackStackName(destId));
mPendingBackStackOperations++;
backStackEffect = BACK_STACK_DESTINATION_ADDED;
}
ft.setReorderingAllowed(true);
ft.commit();
// The commit succeeded, update our view of the world
if(backStackEffect == BACK_STACK_DESTINATION_ADDED) { mBackStack.add(destId); } dispatchOnNavigatorNavigated(destId, backStackEffect); }}Copy the code
The FragmentNavigator structure we saw above when we started NavController. It passes in the childFragmentManager of NavHostFragment. Its navigate operation is the replace operation on FragmentTransaction
2) out of the stack
public class NavController {
public boolean popBackStack(@IdRes int destinationId, boolean inclusive) {
boolean popped = popBackStackInternal(destinationId, inclusive);
// Only return true if the pop succeeded and we've dispatched // the change to a new destination return popped && dispatchOnDestinationChanged(); } boolean popBackStackInternal(@IdRes int destinationId, boolean inclusive) { if (mBackStack.isEmpty()) { // Nothing to pop if the back stack is empty return false; } ArrayList
popOperations = new ArrayList<>(); / / 1. Traverse back stack, record to the stack elements of the Navigator Iterator < NavBackStackEntry > Iterator. = mBackStack descendingIterator (); boolean foundDestination = false; while (iterator.hasNext()) { NavDestination destination = iterator.next().getDestination(); / / to get to the stack elements corresponding to the Navigator. The Navigator Navigator. = mNavigatorProvider getNavigator (destination. GetNavigatorName ()); / / 1.1 records to the stack of elements of the Navigator. The if (inclusive | | destination. The getId ()! = destinationId) { popOperations.add(navigator); } // Terminate if (destination.getid () == destinationId) {foundDestination = true; break; }} if (! foundDestination) { ...... return false; } // 2. Use the Navigator to popBackStack the popBackStack method Boolean popped = false; for (Navigator navigator : PopOperations) {// 2.1 Call navigator's popBackStack if (navigator.popbackstack ()) {// Remove from mBackStack NavBackStackEntry entry = mBackStack.removeLast(); . popped = true; } else { // The pop did not complete successfully, so stop immediately break; } } updateOnBackPressedCallbackEnabled(); return popped; }}
Copy the code
PopUp is similar to an Activity singleTask. If it exists before, it clears all the pages at the top of the stack
- Iterate through the NavController rollback, recording the Navigator to popOperations for the element to be pushed
- Call navigator. popBackStack to remove the stack & remove the last element of mBackStack
Here we also look at the implementation of FragmentNavigator
FragmentNavigator unstack operation
public class FragmentNavigator extends Navigator<FragmentNavigator.Destination> {
@Override
public boolean popBackStack() {
if (mBackStack.isEmpty()) {
return false;
}
if (mFragmentManager.isStateSaved()) {
Log.i(TAG, "Ignoring popBackStack() call: FragmentManager has already"
+ " saved its state");
return false;
}
mFragmentManager.popBackStack(
generateBackStackName(mBackStack.size(), mBackStack.peekLast()),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
mBackStack.removeLast();
return true; }}Copy the code
FragmentNavigator out operation than the stack operation more concise, direct call the mFragmentManager. PopBackStack remove target element
3) review
The details of a navigation process are as follows
- Gets the element at the top of the stack
- Get the NavAction object corresponding to the ID, describing the navigation action
- NavAction is the tag element information that we define in the XML of the navigation diagram
- If only the PopUpId exists, the popBackStack is invokedThe stack,
- Iterate through the NavController rollback, recording the Navigator to popOperations for the element to be pushed
- Call navigator. popBackStack to remove the stack & remove the last element of mBackStack
- FragmentNavigator directly call the mFragmentManager. PopBackStack removes the target element
- If destId is present, the overloaded navigate method is calledInto the stack,
- Get the corresponding Navigator based on the destination’s information
- Call navigator. navigate to the appropriate page
- The FragmentNavigator’s navigate operation is the replace operation on the FragmentTransaction
- If newDest exists, the navigation operation is of the Fragment type and is added to the mBackStack for maintenance
Now that the Navigation component is on and off the stack, how does Navigation distribute return events
Return event distribution
During initialization we see that the onBackPressed listener is registered in the NavHostFragment
public class NavHostFragment extends Fragment implements NavHost { @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); . / / 1. RequireActivity (.) getOnBackPressedDispatcher () to obtain the dispenser / / 2. To a mNavController register callback mNavController. SetOnBackPressedDispatcher (requireActivity () getOnBackPressedDispatcher ()); . } } public class NavController { private final OnBackPressedCallback mOnBackPressedCallback = new OnBackPressedCallback(false) {
@Override
public void handleOnBackPressed() { popBackStack(); }}; voidsetOnBackPressedDispatcher(@NonNull OnBackPressedDispatcher dispatcher) { ...... // It registers a callback dispatcher.addCallback(mLifecycleOwner, mOnBackPressedCallback) for the Activity's mBackPressedCallback; }}Copy the code
When the NavController initializes in the NavHostFragment, it requests the Activity’s return event dispatcher, and the NavController registers a callback to respond to the dispatcher
Let’s first look at how OnBackPressedDispatcher adds a callback
Add listener callback
public final class OnBackPressedDispatcher {
@MainThread
public void addCallback(@NonNull LifecycleOwner owner,
@NonNull OnBackPressedCallback onBackPressedCallback) {
Lifecycle lifecycle = owner.getLifecycle();
if (lifecycle.getCurrentState() == Lifecycle.State.DESTROYED) {
return; } / / for onBackPressedCallback addCancellable onBackPressedCallback. AddCancellable (new LifecycleOnBackPressedCancellable(lifecycle, onBackPressedCallback)); }}Copy the code
Here the implementation of the more interesting, it does not directly to our incoming onBackPressedCallback added to a callback queue, but created LifecycleOnBackPressedCancellable first, Then call the onBackPressedCallback addCancellable method
Let’s look at their implementations separately
1. LifecycleOnBackPressedCancellable created
public final class OnBackPressedDispatcher {
private class LifecycleOnBackPressedCancellable implements LifecycleEventObserver,
Cancellable {
private final Lifecycle mLifecycle;
private final OnBackPressedCallback mOnBackPressedCallback;
@Nullable
private Cancellable mCurrentCancellable;
LifecycleOnBackPressedCancellable(@NonNull Lifecycle lifecycle,
@NonNull OnBackPressedCallback onBackPressedCallback) {
mLifecycle = lifecycle;
mOnBackPressedCallback = onBackPressedCallback;
lifecycle.addObserver(this);
}
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if(event == Lifecycle.Event.ON_START) { // 1. When onStart is called back, Call addCancellableCallback of the external class to inject OnBackPressedCallback into the mOnBackPressedCallbacks queue mCurrentCancellable = addCancellableCallback(mOnBackPressedCallback); }else if (event == Lifecycle.Event.ON_STOP) {
// Should always be non-null
if (mCurrentCancellable != null) {
mCurrentCancellable.cancel();
}
} else if(event == Lifecycle.event.on_destroy) {// automatically remove cancel() from the mOnBackPressedCallbacks queue when onDestroy is removed; } } @Override public voidcancel() {
mLifecycle.removeObserver(this);
mOnBackPressedCallback.removeCancellable(this);
if(mCurrentCancellable ! = null) { mCurrentCancellable.cancel(); mCurrentCancellable = null; } } } final ArrayDeque<OnBackPressedCallback> mOnBackPressedCallbacks = new ArrayDeque<>(); private class OnBackPressedCancellable implements Cancellable { private final OnBackPressedCallback mOnBackPressedCallback; OnBackPressedCancellable(OnBackPressedCallback onBackPressedCallback) { mOnBackPressedCallback = onBackPressedCallback; } @Override public voidcancel() {/ / remove the OnBackPressedCallback mOnBackPressedCallbacks. Remove (mOnBackPressedCallback); / / remove Cancelable mOnBackPressedCallback. RemoveCancellable (this); }} Cancellable addCancellableCallback(@nonnull OnBackPressedCallback OnBackPressedCallback) {// 1.1 will OnBackPressedCallback added to the queue of OnBackPressedDispatcher mOnBackPressedCallbacks. Add (onBackPressedCallback); // 1.2 created an OnBackPressedCancellable to add to the onBackPressedCallback cache // because of the mOnBackPressedCallbacks pair OnBackPressedCallback is not visible, So the purpose of this Cancelable is to help OnBackPressedCancellable remove OnBackPressedCancellable cancellable = from mOnBackPressedCallbacks new OnBackPressedCancellable(onBackPressedCallback); onBackPressedCallback.addCancellable(cancellable);returncancellable; }}Copy the code
LifecycleOnBackPressedCancellable is OnBackPressedDispatcher inner classes
- OnBackPressedCallback is posted to the external class mOnBackPressedCallbacks for subsequent distribution only when the onStart callback is called
- The OnBackPressedCallback is removed from the mOnBackPressedCallbacks when the onDestroy callback is called
In this way, it is possible to automate the addition and release of OnBackPressedCallback callbacks following the lifecycle
2. Add Cancelable to onBackPressedCallback
public abstract class OnBackPressedCallback { private boolean mEnabled; private CopyOnWriteArrayList<Cancellable> mCancellables = new CopyOnWriteArrayList<>(); void addCancellable(@NonNull Cancellable cancellable) { mCancellables.add(cancellable); }}Copy the code
Let’s look at how the Activity to return event distribution to NavController. MOnBackPressedCallback
Return to the event distribution process
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
LifecycleOwner,
ViewModelStoreOwner,
SavedStateRegistryOwner,
OnBackPressedDispatcherOwner {
private final OnBackPressedDispatcher mOnBackPressedDispatcher =
new OnBackPressedDispatcher(new Runnable() {
@Override
public void run() { ComponentActivity.super.onBackPressed(); }}); @Override @MainThread public voidonBackPressed() {
mOnBackPressedDispatcher.onBackPressed();
}
@NonNull
@Override
public final OnBackPressedDispatcher getOnBackPressedDispatcher() {
returnmOnBackPressedDispatcher; }}Copy the code
Return events sent by OnBackPressedDispatcher are supported in ComponentActivity, The implementation agent in onBackPressed gives the onBackPressed method of OnBackPressedDispatcher
public final class OnBackPressedDispatcher {
@Nullable
private final Runnable mFallbackOnBackPressed;
@SuppressWarnings("WeakerAccess") /* synthetic access */
final ArrayDeque<OnBackPressedCallback> mOnBackPressedCallbacks = new ArrayDeque<>();
public OnBackPressedDispatcher() { this(null); } public OnBackPressedDispatcher(@nullable Runnable fallbackOnBackPressed) { It's the final implementation of the Activity's onBackPressed. MFallbackOnBackPressed = fallbackOnBackPressed; } @MainThread public voidonBackPressed() {// 1. Iterator<OnBackPressedCallback> Iterator = mOnBackPressedCallbacks.descendingIterator();while(iterator.hasNext()) { OnBackPressedCallback callback = iterator.next(); // 2. Distribute this event to the first oneenableThe OnBackPressedCallbackif (callback.isEnabled()) {
callback.handleOnBackPressed();
return; }} // 3. If it does not exist, the Activity's onBackPressed is handledif(mFallbackOnBackPressed ! = null) { mFallbackOnBackPressed.run(); }}... }Copy the code
The distribution process is clearer than the listener addition process, and the main steps are as follows
- Backward traverse listens for the callback queue mOnBackPressedCallbacks
- Distribute this event to the first enable OnBackPressedCallback
- If not, the Activity’s onBackPressed handles it
The handleOnBackPressed implementation of OnBackPressedCallback, which we’ve already seen in NavController, calls popBackStack() to perform an out-of-stack operation that won’t be described here
3) review
The distribution of navigational events is supported by OnBackPressedDispatcher in ComponentActivity
- Registration of listener callbacks
- OnBackPressedCallback describe an event monitoring, it will be injected into the OnBackPressedDispatcher. MOnBackPressedCallbacks queue
- Cancelable help onBackPressedCallback it from OnBackPressedDispatcher. MOnBackPressedCallbacks removed
- Distribution of events
- Backward traverse listens for the callback queue mOnBackPressedCallbacks
- Distribute this event to the first enable OnBackPressedCallback
- If not, the Activity’s onBackPressed handles it
- Event handling
- Implemented in NavController, which calls popBackStack to perform an off-stack operation
conclusion
Navigation initialization
The functions of each component of Navigation are as follows
- NavController: Provides interfaces for the upper layer, navigation services and stack maintenance
- NavigationProvider: Maintains the navigator
- Navigator: a Navigator that handles specific jumps and exits
- ActivityNavigator
- FragmentNavigator
- GraphNavigator
Navigation on and off the stack
The details of a navigation process are as follows
- Gets the element at the top of the stack
- Get the NavAction object corresponding to the ID, describing the navigation action
- NavAction is the tag element information that we define in the XML of the navigation diagram
- If only the PopUpId exists, the popBackStack is invokedThe stack,
- Iterate through the NavController rollback, recording the Navigator to popOperations for the element to be pushed
- Call navigator. popBackStack to remove the stack & remove the last element of mBackStack
- FragmentNavigator directly call the mFragmentManager. PopBackStack removes the target element
- If destId is present, the overloaded navigate method is calledInto the stack,
- Get the corresponding Navigator based on the destination’s information
- Call navigator. navigate to the appropriate page
- The FragmentNavigator’s navigate operation is the replace operation on the FragmentTransaction
- If newDest exists, the navigation operation is of the Fragment type and is added to the mBackStack for maintenance
Returns the distribution of events
The distribution of navigational events is supported by OnBackPressedDispatcher in ComponentActivity
- Registration of listener callbacks
- OnBackPressedCallback describe an event monitoring, it will be injected into the OnBackPressedDispatcher. MOnBackPressedCallbacks queue
- Cancelable help onBackPressedCallback it from OnBackPressedDispatcher. MOnBackPressedCallbacks removed
- Distribution of events
- Backward traverse listens for the callback queue mOnBackPressedCallbacks
- Distribute this event to the first enable OnBackPressedCallback
- If not, the Activity’s onBackPressed handles it
- Event handling
- Implemented in NavController, which calls popBackStack to perform an off-stack operation
conclusion
Although Navigation component was contacted relatively early, there was no proper opportunity for me to make efforts to look at it. This time, I read its source code thoroughly, which can be regarded as completing my P2 task
The overall harvest is not small, at least in the selection of technical architecture is another choice, if used, it is more convenient for the subsequent customization and expansion of Navigation