美文网首页
Jetpack--Navigation原理

Jetpack--Navigation原理

作者: 慕北人 | 来源:发表于2021-08-23 18:07 被阅读0次

    Jetpack--Navigation原理

    Jetpack向开发者提供了导航组件来实现Activity或者Fragment的跳转,今天就分析一下使用Navigation来实现Fragment之间的跳转。

    一、相关类介绍

    1. NavHost

    NavHost的定义如下:

    public interface NavHost {
    
        /**
         * Returns the {@link NavController navigation controller} for this navigation host.
         *
         * @return this host's navigation controller
         */
        @NonNull
        NavController getNavController();
    }
    

    NavHost只要求其实现类必须能够通过getNavController()方法返回一个NavController对象。

    而常用的NavHostFragment即为NavHost的一个实现类

    public class NavHostFragment extends Fragment implements NavHost {
        ...
        private NavHostController mNavController;
        private View mViewParent;
    
        // State that will be saved and restored
        private int mGraphId;
        private boolean mDefaultNavHost;
        
        @CallSuper
        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            final Context context = requireContext();
    
            mNavController = new NavHostController(context);
            mNavController.setLifecycleOwner(this);
            mNavController.setOnBackPressedDispatcher(requireActivity().getOnBackPressedDispatcher());
            // Set the default state - this will be updated whenever
            // onPrimaryNavigationFragmentChanged() is called
            mNavController.enableOnBackPressed(
                    mIsPrimaryBeforeOnCreate != null && mIsPrimaryBeforeOnCreate);
            mIsPrimaryBeforeOnCreate = null;
            mNavController.setViewModelStore(getViewModelStore());
            onCreateNavController(mNavController);
    
            ....
        }
    }
    

    可见,NavHostFragment返回的NavController的运行时类型为NavHostController

    同时需要注意的是,NavHostFragment初始化NavController的时机在onCreate方法之中,再次之前调用getNavController方法返回的只是null

    2、NavController

    该类是一个核心类,其内部含有一个导航图对象(NavGraph),该导航图通常是通过NavInflater解析navigation资源文件生成的。NavController通过持有的导航图来控制导航行为,顾名思义,该对象就是一个控制器,控制着导航图、对Fragment或Activity生命周期的管理等,一些核心的属性如下:

    public class NavController {
        private final Context mContext;
        private Activity mActivity;
        private NavInflater mInflater;      // 负责解析导航文件的解析器
        
        @SuppressWarnings("WeakerAccess") /* synthetic access */
        NavGraph mGraph;    // NavInflater解析导航资源文件生成的导航图
        
        private NavigatorProvider mNavigatorProvider = new NavigatorProvider();     // 该类只是一个辅助类,负责保存用到的Navigator对象
        
        @SuppressWarnings("WeakerAccess") /* synthetic access */
        final Deque<NavBackStackEntry> mBackStack = new ArrayDeque<>();
    
        private LifecycleOwner mLifecycleOwner;
        private NavControllerViewModel mViewModel;
    
        private NavigatorProvider mNavigatorProvider = new NavigatorProvider();
    
        private final LifecycleObserver mLifecycleObserver = new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                if (mGraph != null) {
                    for (NavBackStackEntry entry : mBackStack) {
                        entry.handleLifecycleEvent(event);
                    }
                }
            }
        };
    }
    

    通过上小结指导,NavHostFragment的getNavController()返回的NavController对象的运行时类型为:NavHostController

    public class NavHostController extends NavController {
    
        public NavHostController(@NonNull Context context) {
            super(context);
        }
    
        @Override
        public final void setLifecycleOwner(@NonNull LifecycleOwner owner) {
            super.setLifecycleOwner(owner);
        }
    
        @Override
        public final void setOnBackPressedDispatcher(@NonNull OnBackPressedDispatcher dispatcher) {
            super.setOnBackPressedDispatcher(dispatcher);
        }
    
        @Override
        public final void enableOnBackPressed(boolean enabled) {
            super.enableOnBackPressed(enabled);
        }
    
        @Override
        public final void setViewModelStore(@NonNull ViewModelStore viewModelStore) {
            super.setViewModelStore(viewModelStore);
        }
    }
    

    观察NavHostController的源码发现,其并没有什么自己的内容,各个方法全部都是父类的实现,对于该类,我们可以暂且忘记,仅仅当做就是NavController对象即可。

    3、Navigator

    NavController只是管理导航图,导航动作开始于其内部,但是导航动作根本发生地却是在Navigator之中,可以说,Navigator是真正的导航器。Navigator是真正执行导航动作(页面跳转)的地方,其为开发者自动维护了导航所涉及的前后状态

    public abstract class Navigator<D extends NavDestination> {
        @Retention(RUNTIME)
        @Target({TYPE})
        @SuppressWarnings("UnknownNullness") // TODO https://issuetracker.google.com/issues/112185120
        public @interface Name {
            String value();
        }
    
        @NonNull
        public abstract D createDestination();
    
        @Nullable
        public abstract NavDestination navigate(@NonNull D destination, @Nullable Bundle args,
                @Nullable NavOptions navOptions, @Nullable Extras navigatorExtras);
    
        public abstract boolean popBackStack();
    
        @Nullable
        public Bundle onSaveState() {
            return null;
        }
    
        public void onRestoreState(@NonNull Bundle savedState) {
        }
    
        public interface Extras {
        }
    }
    

    可见,Navigator负责的任务主要就是创建目的地,以及navigate到目标页面。

    Google为开发者提供了一些Navigator,在第一部分的NavHostFragment的onCreate()方法中涉及到NavController对象的初始化,以及NavController初始化完毕后调用了方法onCreateNavController(mNavController),这两处都初始化并保存了一些Navigator对象

    NavHostFragment:onCreate()
    public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ....
    
            mNavController = new NavHostController(context);  // NavController构造函数中添加了一些Navigator
            ....
            onCreateNavController(mNavController);   // 该方法内部也添加了一些Navigator
    
            ....
        }
    

    我们先来看看第二个地方的代码。

    NavHostFragment:onCreateNavController(mNavController)
    @CallSuper
        protected void onCreateNavController(@NonNull NavController navController) {
            navController.getNavigatorProvider().addNavigator(
                    new DialogFragmentNavigator(requireContext(), getChildFragmentManager()));
            navController.getNavigatorProvider().addNavigator(createFragmentNavigator());
        }
    
    @Deprecated
        @NonNull
        protected Navigator<? extends FragmentNavigator.Destination> createFragmentNavigator() {
            return new FragmentNavigator(requireContext(), getChildFragmentManager(),
                    getContainerId());
        }
    

    可见在该方法内部实例化并添加了两个Navigator的实现类:

    • DialogFragmentNavigator
    • FragmentNavigator

    而通过名字就能知道,他们一个是负责DialogFragment导航,一个是负责Fragment导航的。

    NavController:NavController()
    public NavController(@NonNull Context context) {
            mContext = context;
            while (context instanceof ContextWrapper) {
                if (context instanceof Activity) {
                    mActivity = (Activity) context;
                    break;
                }
                context = ((ContextWrapper) context).getBaseContext();
            }
            mNavigatorProvider.addNavigator(new NavGraphNavigator(mNavigatorProvider));
            mNavigatorProvider.addNavigator(new ActivityNavigator(mContext));
        }
    

    而在NavController的构造函数中也添加了两种Navigator:

    • NavGraphNavigator
    • ActivityNavigator

    可以肯定后者是为了Activity之间的导航提供支持,而前者猜测是为了为navigation资源文件中导航嵌套之间的导航提供支持的。

    同时也可以知道,这两种Navigator初始化的位置位于NavController的构造器中,而与Fragment导航相关的构造器的添加则位于NavHostFragment之中,合理而又分明。

    4、辅助类

    NavigatorProvider

    该类是一个辅助类,作用只是保存NavController使用到的Navigator对象,在上面的展示中也显示了NavController都是通过其mNavigatorProvider属性来操控Navigator对象的。

    public class NavigatorProvider {
        ....
    
        private final HashMap<String, Navigator<? extends NavDestination>> mNavigators =
                new HashMap<>();
    
        @NonNull
        public final <T extends Navigator<?>> T getNavigator(@NonNull Class<T> navigatorClass) {
            String name = getNameForNavigator(navigatorClass);
            return getNavigator(name);
        }
        
        @SuppressWarnings("unchecked")
        @CallSuper
        @NonNull
        public <T extends Navigator<?>> T getNavigator(@NonNull String name) {
            if (!validateName(name)) {
                throw new IllegalArgumentException("navigator name cannot be an empty string");
            }
    
            Navigator<? extends NavDestination> navigator = mNavigators.get(name);
            if (navigator == null) {
                throw new IllegalStateException("Could not find Navigator with name \"" + name
                        + "\". You must call NavController.addNavigator() for each navigation type.");
            }
            return (T) navigator;
        }
    
        @Nullable
        public final Navigator<? extends NavDestination> addNavigator(
                @NonNull Navigator<? extends NavDestination> navigator) {
            String name = getNameForNavigator(navigator.getClass());
    
            return addNavigator(name, navigator);
        }
    
        @CallSuper
        @Nullable
        public Navigator<? extends NavDestination> addNavigator(@NonNull String name,
                @NonNull Navigator<? extends NavDestination> navigator) {
            if (!validateName(name)) {
                throw new IllegalArgumentException("navigator name cannot be an empty string");
            }
            return mNavigators.put(name, navigator);
        }
    }
    

    很清楚,NavigatorProvider内部通过一个HashMap来保存Navigator对象,key为Navigator的类名

    NavDestination

    根据名字就能够知道,该类代表着navigation资源文件中action中定义的destination fragment(这里的destination fragment的含义为导航图xml文件中作为destination的fragment,即一个destination fragment可能含有多个action,也即一个destination fragment可以通过多个action指定app:destination属性跳转到多个其他的destination fragment);而显然,每一个NavDestination都应该隶属于一个NavGraph

    public class NavDestination {
        ....
        private final String mNavigatorName;
        private NavGraph mParent;       // 指明该NavDestination隶属于的NavGraph对象
        private int mId;
        private String mIdName;
        
        
        private SparseArrayCompat<NavAction> mActions;   // 每一个Destination对应的Fragment可能含有多个Action
        private HashMap<String, NavArgument> mArguments;
    }
    

    NavGraph

    该对象代表了一个导航图,其内部保存了所有的NavDestination节点,前面说到,NavController内部持有一个NavGraph,而NavGraph在NavController内部的作用就是在NavController发出导航指令的时候,NavGraph通过指令传递的id来找到对应的NavDestination节点

    public class NavGraph extends NavDestination implements Iterable<NavDestination> {
        @SuppressWarnings("WeakerAccess") /* synthetic access */
        final SparseArrayCompat<NavDestination> mNodes = new SparseArrayCompat<>();
        private int mStartDestId;
        private String mStartDestIdName;
        
        public final void addDestination(@NonNull NavDestination node) {
            if (node.getId() == 0) {
                throw new IllegalArgumentException("Destinations must have an id."
                        + " Call setId() or include an android:id in your navigation XML.");
            }
            NavDestination existingDestination = mNodes.get(node.getId());
            if (existingDestination == node) {
                return;
            }
            if (node.getParent() != null) {
                throw new IllegalStateException("Destination already has a parent set."
                        + " Call NavGraph.remove() to remove the previous parent.");
            }
            if (existingDestination != null) {
                existingDestination.setParent(null);
            }
            node.setParent(this);
            mNodes.put(node.getId(), node);
        }
    
        public final void addDestinations(@NonNull Collection<NavDestination> nodes) {
            for (NavDestination node : nodes) {
                if (node == null) {
                    continue;
                }
                addDestination(node);
            }
        }
    
        public final void addDestinations(@NonNull NavDestination... nodes) {
            for (NavDestination node : nodes) {
                if (node == null) {
                    continue;
                }
                addDestination(node);
            }
        }
    
        @Nullable
        public final NavDestination findNode(@IdRes int resid) {
            return findNode(resid, true);
        }
    
        @Nullable
        final NavDestination findNode(@IdRes int resid, boolean searchParents) {
            NavDestination destination = mNodes.get(resid);
            // Search the parent for the NavDestination if it is not a child of this navigation graph
            // and searchParents is true
            return destination != null
                    ? destination
                    : searchParents && getParent() != null ? getParent().findNode(resid) : null;
        }
    }
    

    首先可以看到,mNodes对象就是保存该导航图下所有NavDestination节点的容器。而且,NavGraph也提供了一些addDestination和findNode的方法用来保存和获取NavDestination对象。

    注:NavGraph的addDestination()方法的调用时机为NavHostFragment的onInflate中,在解析导航资源文件的时候会直接将解析得到的NavDestination加入到NavGraph中。

    这里有一个稍微有一些绕的地方,那就是NavGraph的作用是保存导航图中的NavDestination节点,但是他自己本身却是NavDestination的子类,总归有些奇怪。个人认为,这是一种不得已,因为在导航图的资源文件中可以存在navigation嵌套的情况,而这种情况下势必存在一个NavGraph作为NavDestination的角色出现,所以NavGraph是NavDestination就显得不得已而为之了。

    NavAction

    上面提到一个NavDestination也即一个可能的目的地,而一个目的地又可以有多个Action跳转到其他的目的地,因此,在NavDestination中含有保存所有其下action的属性。而该类显然就是navigation资源文件中的Action的封装了,且看:

    public final class NavAction {
        @IdRes
        private final int mDestinationId;
        private NavOptions mNavOptions;
        private Bundle mDefaultArguments;
        ....
    }
    

    根据变量就能够知道上述变量的作用。

    NavOptions

    该类为navigation资源文件中每一个Action标签下的一些选项属性:

    public final class NavOptions {
        private boolean mSingleTop;
        @IdRes
        private int mPopUpTo;
        private boolean mPopUpToInclusive;
        @AnimRes @AnimatorRes
        private int mEnterAnim;
        @AnimRes @AnimatorRes
        private int mExitAnim;
        @AnimRes @AnimatorRes
        private int mPopEnterAnim;
        @AnimRes @AnimatorRes
        private int mPopExitAnim;
    }
    

    小结

    经过上述的介绍,我们可以简单得到一个UML图:


    未命名文件 (3).png

    可以很明显的看出NavController的管理作用:

    • NavGraph管理着navigation资源文件中定义的destination fragment,并将其封装成NavDestination;而NavDestination则将资源文件中的action标签封装成NavAction对象,而NavAction对象中包含了可以在资源文件中定义的各个options设置;
    • Navigator则负责导航动作的执行,该对象通过NavigatorProvider对象的存取功能向NavController提供服务。
    • NavController为导航发起的位置,协调NavGraph与Navigator共同完成导航功能。

    二、导航原理

    在使用导航组件进行导航时,简单用法如下:

    NavHostFragment navHost =(NavHostFragment) getActivity().getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment_jounal_item_detail);     navHost.getNavController().navigate(R.id.action_DetailFragment_to_ImageShowFragment, bundle);
    

    其中要求layout文件中id对应的fragment必须为Google提供的NavHostFragment,因为目前只有该Fragment对象实现了NavHost接口,才能初始化NavController。

    执行跳转时,很显然调用的是NavHostController的navigate方法,而上面展示到NavHostController并没有对父类NavController中的方法进行重写,只是一个空壳子类,所以navigate方法的实现需要看NavController。

    1、NavController::navigate

    经过回调,最终会调用到含四个参数的navigate方法:

    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);
        
        //  ----------------------注释 2-----------------------------
            Bundle combinedArgs = null;
            if (navAction != null) {
                if (navOptions == null) {
                    // ------------------a---------------------
                    navOptions = navAction.getNavOptions();
                }
                destId = navAction.getDestinationId();
                Bundle navActionArgs = navAction.getDefaultArguments();
                if (navActionArgs != null) {
                    combinedArgs = new Bundle();
                    // -------------------b---------------------
                    combinedArgs.putAll(navActionArgs);
                }
            }
    
            if (args != null) {
                if (combinedArgs == null) {
                    combinedArgs = new Bundle();
                }
                // -----------------c------------------------
                combinedArgs.putAll(args);
            }
    
            if (destId == 0 && navOptions != null && navOptions.getPopUpTo() != -1) {
                popBackStack(navOptions.getPopUpTo(), navOptions.isPopUpToInclusive());
                return;
            }
    
           ...
            // ---------------------注释 3---------------------------
            NavDestination node = findDestination(destId);
            ...
            navigate(node, combinedArgs, navOptions, navigatorExtras);
        }
    

    该方法的流程被分为三个大的部分:

    1. 注释1处:该部分的意义是首先获取当前位于的destination fragment对应的NavDestination对象,该对象的获取有两种情况:首先是刚启动时,当前的Fragment还是NavHostFragment,此时mBackStack还是空的,因此此时获取到的是mGraph对象;之后,当navigation资源文件中的startDestination对应的NavDestination被添加到mBackStack后,获取当前destination fragment时就是从mBackStack中了。得到了当前destination fragment之后,根据actionid获取到对应的NavAction对象(前面说过,NavDestination中将资源文件中action标签都封装成了NavAction对象保存了起来);
    2. 注释2处:这部分的含义为配置参数,需要配置的参数由NavOptions与Argument,而二者配置的思想是一致的。通过a处的代码可以发现,最终的NavOptions中navigate方法的参数navOptions的优先级是高于NavAction中的Options的(即xml文件中定义的静态Options);而通过bc处的代码可以知道,最终的Argument是结合了navigate方法的参数args和NavAction中的defaultArgument的,但是由于args中的值是后面添加的,所以如果存在重复的话,args会覆盖掉defaultArgument,所以依然是动态添加的内容优先级高
    3. 注释3处:这部分首先来获取本次导航的目的destination fragment对应的NavDestination对象,然后执行另一个navigate方法。

    2、NavController::navigate

    该方法虽然同名,但是第一个参数变为了目的destination fragment对应的NavDestination:

    private void navigate(@NonNull NavDestination node, @Nullable Bundle args,
                @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
            ....
            Bundle finalArgs = node.addInDefaultArgs(args);
            NavDestination newDest = navigator.navigate(node, finalArgs,
                    navOptions, navigatorExtras);
            if (newDest != null) {
                ...
                // And finally, add the new destination with its default args
                NavBackStackEntry newBackStackEntry = new NavBackStackEntry(mContext, newDest,
                        newDest.addInDefaultArgs(finalArgs), mLifecycleOwner, mViewModel);
                mBackStack.add(newBackStackEntry);
            } else if (navOptions != null && navOptions.shouldLaunchSingleTop()) {
                launchSingleTop = true;
                NavBackStackEntry singleTopBackStackEntry = mBackStack.peekLast();
                if (singleTopBackStackEntry != null) {
                    singleTopBackStackEntry.replaceArguments(args);
                }
            }
            updateOnBackPressedCallbackEnabled();
            if (popped || newDest != null || launchSingleTop) {
                dispatchOnDestinationChanged();
            }
        }
    

    该方法就显得很简单了:

    1. 首先是调用Navigator对象的navigate方法执行导航动作;

    2. 根据上一步的结果进行处理:

      1. 成功得到了一个NavDestination对象,说明该对象不是singleTop启动或者是singleTop启动但为首次启动,这都说明了该对象应该添加到mBackStack中,第一个if语句就是这个意思;
      2. 而else if则说明是singleTop启动,并且mBackStack中已经有了该NavDestination并且位于top位置,说明是singleTop启动,那么执行的动作是使用replaceArgument来替换掉原来的数据

    最后,代码的关键就是看Navigator.navigate了,其内部是如何执行的?以及,何时返回null?

    3、Navigator::navigate

    根据上面的分析可知,我们本场景下的Navigator的具体类为:FragmentNavigator,为此,我们看看他的navigate方法实现:

    public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args,
                @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
            ....
                // ------------------注释 1---------------------
            String className = destination.getClassName();
            if (className.charAt(0) == '.') {
                className = mContext.getPackageName() + className;
            }
            final Fragment frag = instantiateFragment(mContext, mFragmentManager,
                    className, args);
            frag.setArguments(args);
            final FragmentTransaction ft = mFragmentManager.beginTransaction();
    
        // ----------------------注释 2-----------------------
            int enterAnim = navOptions != null ? navOptions.getEnterAnim() : -1;
            int exitAnim = 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);
            }
    
        // -------------------注释 3-----------------------
            ft.replace(mContainerId, frag);
            ft.setPrimaryNavigationFragment(frag);
    
        // ----------------------注释 4-----------------------
            final @IdRes int destId = destination.getId();
            final boolean initialNavigation = mBackStack.isEmpty();
            // TODO Build first class singleTop behavior for fragments
            final boolean isSingleTopReplacement = navOptions != null && !initialNavigation
                    && navOptions.shouldLaunchSingleTop()
                    && mBackStack.peekLast() == destId;
    
            boolean isAdded;
        
        // --------------------------a----------------------------
            if (initialNavigation) {
                isAdded = true;
            } else if (isSingleTopReplacement) {
                // --------------------------b----------------------------
                // Single Top means we only want one instance on the back stack
                if (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(
                            generateBackStackName(mBackStack.size(), mBackStack.peekLast()),
                            FragmentManager.POP_BACK_STACK_INCLUSIVE);
                    ft.addToBackStack(generateBackStackName(mBackStack.size(), destId));
                }
                isAdded = false;
            } else {
                // --------------------------c----------------------------
                ft.addToBackStack(generateBackStackName(mBackStack.size() + 1, destId));
                isAdded = true;
            }
            ....
            ft.setReorderingAllowed(true);
            ft.commit();
        
            // The commit succeeded, update our view of the world
            if (isAdded) {
                mBackStack.add(destId);
                return destination;
            } else {
                return null;
            }
        }
    

    对于该方法,明显也可以分为如下几个部分:

    1. 步骤1:该部分很明确,就是通过类名利用反射来创建一个Fragment对象,然后为Fragment设置Argument,同时开启一个Fragment事务
    2. 步骤2:这部分的含义是为Fragment配置动画等信息;
    3. 步骤3:利用Fragment事务执行replace方法,将目标Fragment替换到页面上;
    4. 步骤4:这一步又是专门对singleTop启动的处理,首先isSingleTopReplacement变量的含义为:当前导航的启动模式为singleTop,并且目destination fragment就在mBackStack的top位置,所以此时调用了FragmentManager的popBackStack来撤销上一次的replace事务操作(根据意义可知上一次replace操作证实将目标destination fragment加入到了Fragment栈中,所以这里的意义是将其从栈中剔除,那么步骤3的replace就会再次将其加入到回退栈中,实现了singleTop),虽说FragmentManager管理的Fragment回退栈中的top位置的Fragment已经重新replace了,但是在Navigator的mBackStack中对应的NavDestination确没有必要剔除再加入新的,所以这一步之后isAdd变量为false,这也导致了在NavController调用Navigator.navigate返回的最终是null,从而引发NavController在管理自己的mBackStack的行为的不同。而ac处的代码就是表明需要在Navigator的mBackStack中添加。

    三、总结

    总的来说,Navigation组件实现Fragment之间的跳转本质还是通过FragmentManager + FragmentTransaction的组合方式来实现的。

    优点

    使用导航组件实现Fragment之间的跳转的优点在于:

    1. 逻辑清晰且代码整洁:页面之间的跳转关系定义在navigation资源文件中,使得跳转关系一目了然,避免了写在Activity之中混乱不堪,分布杂乱的问题;同时,封装了的FragmentManager与FragmentTransaction也进一步解放了开发者。
    2. 功能丰富:最明显的就是提供了Fragment跳转的singleTop启动支持,通过上面的分析可以知道Navigation实现Fragment的singleTop功能有两点:首先,利用Navigator的mBackStack数据结构来判断是否存在目标Fragment以及是否已经位于top位置,这是FragmentManager的Fragment回退栈没法做到的事情;其次,实现singleTop功能利用FragmentManager撤销上一次FragmentTransaction和添加新一次FragmentTransaction的replace来实现(同样可以发现,Fragment的singleTop只有当目标Fragment正好位于top位置是才会生效,如果位于回退栈的非top位置,那么便不会有作用)

    问题

    1. 可以发现Navigator与NavController都各自维护了一个mBackStack,感觉有些多余?二者只维护一个不可以吗?

    相关文章

      网友评论

          本文标题:Jetpack--Navigation原理

          本文链接:https://www.haomeiwen.com/subject/mbmliltx.html