【Jetpack】Navigation

一、使用

最全面的Navigation的使用指南

Navigation(一)基础入门

Navigation(二)使用safe args传递参数

Navigation(三)NavigationUI的使用

二、源码解析

NavHostFragment的创建

官网上设置nav的xml文件

 val finalHost = NavHostFragment.create(R.navigation.nav_graph_main);
        supportFragmentManager.beginTransaction()
                .replace(R.id.ll_fragment_navigation, finalHost)
                .setPrimaryNavigationFragment(finalHost)
                .commit();

或者重写

   @Override
    public boolean onSupportNavigateUp() {
        Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.my_nav_host_fragment);
        return NavHostFragment.findNavController(fragment).navigateUp();
    }


1、NavHostFragment.create方法 
(1)初始化Bundle,并且将graphResId,startDestinationArgs存储在Bundle中。 
(2)new NavHostFragment()返回NavHostFragment实例。

    @NonNull
    public static NavHostFragment create(@NavigationRes int graphResId,
            @Nullable Bundle startDestinationArgs) {
        Bundle b = null;
        if (graphResId != 0) {
            b = new Bundle();
            b.putInt(KEY_GRAPH_ID, graphResId);
        }
        if (startDestinationArgs != null) {
            if (b == null) {
                b = new Bundle();
            }
            b.putBundle(KEY_START_DESTINATION_ARGS, startDestinationArgs);
        }

        final NavHostFragment result = new NavHostFragment();
        if (b != null) {
            result.setArguments(b);
        }
        return result;
    }

然就在NavHostFragment 里有许多生命周期的方法会跟随fragment的方法进行触发

2、XML文件的解析:主要是解析布局文件的两个属性。defaultNavHost和navGraph,并且初始化全局变量NavHostFragment.onInflate方法 当Fragment以XML的方式静态加载时,最先会调用onInflate的方法
(调用时机:Fragment所关联的Activity在执行setContentView时)

将FragmentContainerView下的defaultNavHost和navGraph解析出来

    @CallSuper
    @Override
    public void onInflate(@NonNull Context context, @NonNull AttributeSet attrs,
            @Nullable Bundle savedInstanceState) {
        super.onInflate(context, attrs, savedInstanceState);

        final TypedArray navHost = context.obtainStyledAttributes(attrs,
                androidx.navigation.R.styleable.NavHost);
        final int graphId = navHost.getResourceId(
                androidx.navigation.R.styleable.NavHost_navGraph, 0);
        if (graphId != 0) {
            mGraphId = graphId;
        }
        navHost.recycle();

        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NavHostFragment);
        final boolean defaultHost = a.getBoolean(R.styleable.NavHostFragment_defaultNavHost, false);
        if (defaultHost) {
            mDefaultNavHost = true;
        }
        a.recycle();
    }

3、onCreateNavController方法中 

先记住帮我们造了一个导航的提供者NavigatorProvider,他是一个导航器,保存各种导航,其中mNavigatorProvider是NavController中的全局变量,内部通过HashMap键值对的形式保存Navigator类。createFragmentNavigator方法,构建了FragmentNavigator对象,其中抽象类Navigator还有个重要的实现类ActivityNavigator和NavGraphNavigator。这个两个类的对象在NavController的构造方法中被添加。

其中Navigator类的作用是:能够实例化对应的NavDestination,并且能够实现导航功能,拥有自己的回退栈。

    @CallSuper
    protected void onCreateNavController(@NonNull NavController navController) {
        navController.getNavigatorProvider().addNavigator(
                new DialogFragmentNavigator(requireContext(), getChildFragmentManager()));
        navController.getNavigatorProvider().addNavigator(createFragmentNavigator());
    }

private NavigatorProvider mNavigatorProvider = new NavigatorProvider();

   @NonNull
    public NavigatorProvider getNavigatorProvider() {
        return mNavigatorProvider;
    }

创建的FragmentNavigator

   @Deprecated
    @NonNull
    protected Navigator<? extends FragmentNavigator.Destination> createFragmentNavigator() {
        return new FragmentNavigator(requireContext(), getChildFragmentManager(),
                getContainerId());
    }

4、接下来执行onCreate

无论是XML实现还是代码实现,都会执行Fragment的onCreate方法.
NavController在这里被创建,并且NavHostFragment中有一个NavController对象。

(1)初始化NavController,NavController为导航的控制类,核心类。
        mNavController = new NavHostController(context);

(2)if (savedInstanceState != null) {开始恢复状态}

(3)if (mGraphId != 0) {设置导航图信息}

   @CallSuper
    @Override
    public void onCreate(@Nullable Bundle 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);

        Bundle navState = null;
//恢复数据
        if (savedInstanceState != null) {
            navState = savedInstanceState.getBundle(KEY_NAV_CONTROLLER_STATE);
            if (savedInstanceState.getBoolean(KEY_DEFAULT_NAV_HOST, false)) {
                mDefaultNavHost = true;
                getParentFragmentManager().beginTransaction()
                        .setPrimaryNavigationFragment(this)
                        .commit();
            }
            mGraphId = savedInstanceState.getInt(KEY_GRAPH_ID);
        }

        if (navState != null) {
            // Navigation controller state overrides arguments
            mNavController.restoreState(navState);
        }
//id在xml里面加了。就要设置id进去
        if (mGraphId != 0) {
            // Set from onInflate()
            mNavController.setGraph(mGraphId);
        } else {
            // See if it was set by NavHostFragment.create()
            final Bundle args = getArguments();
            final int graphId = args != null ? args.getInt(KEY_GRAPH_ID) : 0;
            final Bundle startDestinationArgs = args != null
                    ? args.getBundle(KEY_START_DESTINATION_ARGS)
                    : null;
            if (graphId != 0) {
                mNavController.setGraph(graphId, startDestinationArgs);
            }
        }

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

接下来研究controller

mNavController.setGraph(mGraphId)

解析刚才的xml文件,第二个参数是启动的是谁,xml里设置过

.setGraph()方法

构建NavGraph
在构建NavController的时候,我们还调用了NavController.setGraph(graphId)方法,该方法主要是构建NavGraph。调用getNavInflater方法创建NavInflater对象,用于解析navigation xml

  @CallSuper
    public void setGraph(@NavigationRes int graphResId, @Nullable Bundle startDestinationArgs) {
        setGraph(getNavInflater().inflate(graphResId), startDestinationArgs);
    }

NavInflater.inflate方法
根据传入的XML资源id构建NavGraph,NavGraph组成Fragment路由的导航地图,而NavDestination代表了导航的每一个目的地。在解析完NavDestination后,需要要求NavDestination为NavGraph,即NavGraph是NavDestination的子类。而且在NavGraph内部存储了NavDestination信息。
 

 @SuppressLint("ResourceType")
    @NonNull
    public NavGraph inflate(@NavigationRes int graphResId) {
        Resources res = mContext.getResources();
        XmlResourceParser parser = res.getXml(graphResId);
        final AttributeSet attrs = Xml.asAttributeSet(parser);
        try {
            int type;
            while ((type = parser.next()) != XmlPullParser.START_TAG
                    && type != XmlPullParser.END_DOCUMENT) {
                // Empty loop
            }
            if (type != XmlPullParser.START_TAG) {
                throw new XmlPullParserException("No start tag found");
            }

            String rootElement = parser.getName();
//拿到目的地
            NavDestination destination = inflate(res, parser, attrs, graphResId);
            if (!(destination instanceof NavGraph)) {
                throw new IllegalArgumentException("Root element <" + rootElement + ">"
                        + " did not inflate into a NavGraph");
            }
            return (NavGraph) destination;
        } catch (Exception e) {
            throw new RuntimeException("Exception inflating "
                    + res.getResourceName(graphResId) + " line "
                    + parser.getLineNumber(), e);
        } finally {
            parser.close();
        }
    }

上面的inflate方法内部会继续调用inflate方法。 
(1)getNavigator方法获取都Navigator实例,该实例在构建NavController是被添加进去,这里获取的是FragmentNavigator对象。
(2)createDestination方法,会调用FragmentNavigator的createDestination构建Destination对象。
(3)onInflate方法,解析destination XML
(4)while循环内部通过递归构建导航图。

    @CallSuper
    public void setGraph(@NonNull NavGraph graph, @Nullable Bundle startDestinationArgs) {
        if (mGraph != null) {
            // Pop everything from the old graph off the back stack
            popBackStackInternal(mGraph.getId(), true);
        }
        mGraph = graph;
        onGraphCreated(startDestinationArgs);
    }


通过NavInflater类之后,解析了XML文件构建整个Graph之后。下面回到setGraph方法,在解析玩XML后会,回到NavHostFragment.setGraph方法。
(1)popBackStackInternal方法将回退栈中的信息全部出栈。
(2)调用onGraphCreated主要是显示一个导航Fragment视图。
 

private void onGraphCreated(@Nullable Bundle startDestinationArgs) {
     ......
        if (mGraph != null && mBackStack.isEmpty()) {
            boolean deepLinked = !mDeepLinkHandled && mActivity != null
                    && handleDeepLink(mActivity.getIntent());
            if (!deepLinked) {
                // Navigate to the first destination in the graph
                // if we haven't deep linked to a destination
//导航到第一个目的地,点进去,可以看到加载第一个页面的过程,ui就看到第一个页面
                navigate(mGraph, startDestinationArgs, null, null);
            }
        } else {
            dispatchOnDestinationChanged();
        }
    }

5、onCreateView

NavHostFragment.onCreateView方法 
该NavHostFragment的视图就只有一个FragmentContainerView extends FrameLayout

containerView.setId(getContainerId());//这行主要用于以代码方式添加fragment

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        FragmentContainerView containerView = new FragmentContainerView(inflater.getContext());
        // When added via XML, this has no effect (since this FragmentContainerView is given the ID
        // automatically), but this ensures that the View exists as part of this Fragment's View
        // hierarchy in cases where the NavHostFragment is added programmatically as is required
        // for child fragment transactions
        containerView.setId(getContainerId());
        return containerView;
    }

6、onViewCreated

  @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        if (!(view instanceof ViewGroup)) {
            throw new IllegalStateException("created host view " + view + " is not a ViewGroup");
        }
        Navigation.setViewNavController(view, mNavController);
        // 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) {
            mViewParent = (View) view.getParent();
            if (mViewParent.getId() == getId()) {
                Navigation.setViewNavController(mViewParent, mNavController);
            }
        }
    }

至此、将第一页显示出来,然后相关的类初始化完成

调用

1.获取NavController
NavHostFragment.findNavController(fragment)
2.findNavController方法 该方法没什么实质性的代码,只要是调用了findViewNavController方法。
3.findViewNavController方法 通过view.tag查找NavController。内部调用了getViewNavController方法。
4.getViewNavController方法 通过获取view的Tag,获取NavController对象,这里的tag ID和setViewNavController都是nav_controller_view_tag。

Navigation.findNavController(view).navigate(R.id.action_page3);
    @NonNull
    public static NavController findNavController(@NonNull View view) {
        NavController navController = findViewNavController(view);
        if (navController == null) {
            throw new IllegalStateException("View " + view + " does not have a NavController set");
        }
        return navController;
    }

拿到controller,调用

1.在构建和获取到NavController对象以及NavGraph之后。,
下面是使用它来实现真正的导航了。下面从navigate开始分析。在navigate方法内部会查询到NavDestination,然后根据不同的Navigator实现页面导航。

navigate 方法
(1)如果回退栈为null返回NavGraph,不为null返回回退栈中的最后一项。  
    NavDestination currentNode = mBackStack.isEmpty() ? mGraph : mBackStack.getLast().getDestination();
(2)根据id,获取对应的NavAction。然后在通过NavAction获取目的地id。
    final NavAction navAction = currentNode.getAction(resId); 
    destId = navAction.getDestinationId();
(4)利用目的地ID属性,通过findDestination方法,找到准备导航的目的地。 
    NavDestination node = findDestination(destId);
(5)开始导航
    1.navigate(node, combinedArgs, navOptions, navigatorExtras);
    2.NavDestination newDest = navigator.navigate(node, finalArgs,navOptions, navigatorExtras);
 

  public void navigate(@IdRes int resId, @Nullable Bundle args, @Nullable NavOptions navOptions,
            @Nullable Navigator.Extras navigatorExtras) {
        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) {
            if (navOptions == null) {
                navOptions = navAction.getNavOptions();
            }
//拿到下一个目的地
            destId = navAction.getDestinationId();
            Bundle navActionArgs = navAction.getDefaultArguments();
            if (navActionArgs != null) {
                combinedArgs = new Bundle();
                combinedArgs.putAll(navActionArgs);
            }
        }

       ...
//进行初始化时的跳转操作

        navigate(node, combinedArgs, navOptions, navigatorExtras);
    }

这里的操作有一个进栈的过程,所以会记录上一个进栈的过程

(1)NavHostFragment 作为导航载体,在Activity的layout文件里被引用(或者在代码中动态),并且持有导航控制类NavController引用。
(2)NavController 将导航任务委托给Navigator类,Navigator类有两个重要的子类FragmentNavigator和ActivityNavigator子类。NavController类持有NavInflater类引用。
(3)NavInflater 负责解析Navgation文件,负责构建NavGraph导航图。
(4)NavDestination 存有各个目的地信息,在FragmentNavigator和ActivityNavigator内部分别对应一个Destination类,该类继承NavDestination。
(5)在页面导航时,fragment的操作还是交由FragmentManager在操作,activity交由startActivity执行。
 

NavHostFragment 里使用navController初始化的时候使用解析器将整张图信息解析出来, 也即使minflterNavinflater 里持有NavGraph

用控制器访问目标位置NavDestination,调用navigate走到Navigator ,调用FragmentNew来显示。同时在栈内记录一次

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值