Navigation(一)

Android知识总结

前言

Navigation 结构图

一、实例化 NavHostFragment

NavHostFragment#create

@NonNull
public static NavHostFragment create(@NavigationRes int graphResId) {
    return create(graphResId, null);
}

@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); // 把graphID存入到Bundle
    }
    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;
}
@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); // 会把我们的 mNavController 加入到 Navigation里面去

    // 说白了,如果是代码的方式去写的话,并非是xml的写法的话,就做下面的代码,但是 大部分都是 xml的形式
    // 当以编程方式添加时,我们需要在父级上设置 NavController - 即具有与此 NavHostFragment 匹配的 ID 的视图
    // 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);
        }
    }
}

二、生命周期流程

NavHostFragment.onInflate开始

    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);
        //fragment 控制器集合
        final int graphId = navHost.getResourceId(
                androidx.navigation.R.styleable.NavHost_navGraph, 0);
        // 就把此属性解析出来,activity_main.xml 的 app:navGraph="@navigation/nav_graph_main"
        if (graphId != 0) {
            mGraphId = graphId;
        }
        navHost.recycle();

        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NavHostFragment);
        //拦截系统back键
        final boolean defaultHost = a.getBoolean(R.styleable.NavHostFragment_defaultNavHost, false);
        // 就把此属性解析出来,activity_main.xml 的 app:defaultNavHost="true"
        if (defaultHost) {
            mDefaultNavHost = true;
        }
        a.recycle();
    }

三、创建控制器

@CallSuper
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    final Context context = requireContext();
    // 此处 实例化出 NavHostController 控制器
    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);
    }
    if (mGraphId != 0) { // 如果GraphID不等于空
        // Set from onInflate()
        // 设置GraphID,此处意义重大,会获取nav_graph_main里面的action等导航信息
        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);
        }
    }
    super.onCreate(savedInstanceState);
}

调用NavHostFragment.onCreateNavController方法

private NavigatorProvider mNavigatorProvider = new NavigatorProvider();

@CallSuper
protected void onCreateNavController(@NonNull NavController navController) {
    // 这里是通过导航暴露者,获取到 NavigationProvider 对象
    navController.getNavigatorProvider().addNavigator(
            new DialogFragmentNavigator(requireContext(), getChildFragmentManager()));
    navController.getNavigatorProvider().addNavigator(createFragmentNavigator());
}

public NavigatorProvider getNavigatorProvider() {
      return mNavigatorProvider;
}

创建 fragment 导航

protected Navigator createFragmentNavigator() {
    // 此ID,如果不写xml文件,单纯用代码实现的时候,需要得到一个父容器ID
    return new FragmentNavigator(requireContext(), getChildFragmentManager(), getContainerId()); 
}

private int getContainerId() {
    int id = getId();
    if (id != 0 && id != View.NO_ID) {
        return id;
    }
    return R.id.nav_host_fragment_container;
}

四、NavigatorProvider 导航管理器

  • NavigatorProvider导航控制器类中进行保存 fragment 的导航类
public class NavigatorProvider {
    // 把导航页面,例如 三个Fragment保存  与  key保存 记录
    private static final HashMap, String> sAnnotationNames = new HashMap<>();
    //存放导航集合
    private final HashMap> mNavigators = new HashMap<>();

    //获取导航的 name ,并保持到集合 sAnnotationNames 中
    @NonNull
    static String getNameForNavigator(@NonNull Class navigatorClass) {
        //从集合中获取对象
        String name = sAnnotationNames.get(navigatorClass);
        if (name == null) {
            //获取 name  注解
            Navigator.Name annotation = navigatorClass.getAnnotation(Navigator.Name.class);
            //获取值 name(导航)
            name = annotation != null ? annotation.value() : null;
            if (!validateName(name)) {
                throw new IllegalArgumentException("No @Navigator.Name annotation found for "
                        + navigatorClass.getSimpleName());
            }
            //把name(导航)保存到集合中
            sAnnotationNames.put(navigatorClass, name);
        }
        return name;
    }

    //添加导航
    @Nullable
    public final Navigator addNavigator(
            @NonNull Navigator navigator) {
        //navigator 是 DialogFragmentNavigator 的实例
        String name = getNameForNavigator(navigator.getClass());

        return addNavigator(name, navigator);
    }

    //获取导航
    public > T getNavigator(@NonNull String name) {
        if (!validateName(name)) {
            throw new IllegalArgumentException("navigator name cannot be an empty string");
        }
        //根据 name 获取导航
        Navigator 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;
    }
}

五、导航页面 Navigator

Navigator是作为中间层解决高耦合低扩展的问题
实现类有:

  • DialogFragmentNavigator
  • FragmentNavigator
  • ActivityNavigator
  • NavGraphNavigator
  • NoOpNavigator

5.1、DialogFragmentNavigator 导航

  • 创建导航页面 Navigator,我们按上面的DialogFragmentNavigator 分析
//注解 Navigator.Name 主要获取那个导航
@Navigator.Name("dialog")
public final class DialogFragmentNavigator extends Navigator {
    private final Context mContext;
    private final FragmentManager mFragmentManager;
    private int mDialogCount = 0;
    private final HashSet mRestoredTagsAwaitingAttach = new HashSet<>();

    public DialogFragmentNavigator(@NonNull Context context, @NonNull FragmentManager manager) {
        mContext = context;
        mFragmentManager = manager;
    }

    public NavDestination navigate(@NonNull final Destination destination, @Nullable Bundle args,
            @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
        if (mFragmentManager.isStateSaved()) {
            Log.i(TAG, "Ignoring navigate() call: FragmentManager has already"
                    + " saved its state");
            return null;
        }
        String className = destination.getClassName();
        if (className.charAt(0) == '.') {
            className = mContext.getPackageName() + className;
        }
        final Fragment frag = mFragmentManager.getFragmentFactory().instantiate(
                mContext.getClassLoader(), className);
        if (!DialogFragment.class.isAssignableFrom(frag.getClass())) {
            throw new IllegalArgumentException("Dialog destination " + destination.getClassName()
                    + " is not an instance of DialogFragment");
        }
        final DialogFragment dialogFragment = (DialogFragment) frag;
        dialogFragment.setArguments(args);
        //绑定 lifecycle
        dialogFragment.getLifecycle().addObserver(mObserver);
        //显示fragment
        dialogFragment.show(mFragmentManager, DIALOG_TAG + mDialogCount++);

        return destination;
    }

    public static class Destination extends NavDestination implements FloatingWindow {

        private String mClassName;

        public Destination(@NonNull NavigatorProvider navigatorProvider) {
            this(navigatorProvider.getNavigator(DialogFragmentNavigator.class));
        }

        public Destination(@NonNull Navigator fragmentNavigator) {
            super(fragmentNavigator);
        }

        @CallSuper
        @Override
        public void onInflate(@NonNull Context context, @NonNull AttributeSet attrs) {
            super.onInflate(context, attrs);
            TypedArray a = context.getResources().obtainAttributes(attrs,
                    R.styleable.DialogFragmentNavigator);
            String className = a.getString(R.styleable.DialogFragmentNavigator_android_name);
            if (className != null) {
                setClassName(className);
            }
            a.recycle();
        }

        @NonNull
        public final Destination setClassName(@NonNull String className) {
            mClassName = className;
            return this;
        }

        @NonNull
        public final String getClassName() {
            if (mClassName == null) {
                throw new IllegalStateException("DialogFragment class was not set");
            }
            return mClassName;
        }
    }
}

5.2、FragmentNavigator 导航

public class FragmentNavigator extends Navigator {
    private static final String TAG = "FragmentNavigator";
    private static final String KEY_BACK_STACK_IDS = "androidx-nav-fragment:navigator:backStackIds";

    private final Context mContext;
    private final FragmentManager mFragmentManager;
    private final int mContainerId;
    private ArrayDeque mBackStack = new ArrayDeque<>();

    public FragmentNavigator(@NonNull Context context, @NonNull FragmentManager manager,
            int containerId) {
        mContext = context;
        mFragmentManager = manager;
        mContainerId = containerId;
    }

    //创建目的地
    public Destination createDestination() {
        return new Destination(this);
    }

}
  • FragmentNavigator#navigate导航
@Nullable
@Override
public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args,
        @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
    if (mFragmentManager.isStateSaved()) {
        Log.i(TAG, "Ignoring navigate() call: FragmentManager has already"
                + " saved its state");
        return null;
    }
   
    // 根据Destination目的地,获取到第一个Fragmet  也就是 MainPage1Fragment实例
    // 下面代码是反射 去 实例化 我们的 第一个Fragment
    String className = destination.getClassName();
    if (className.charAt(0) == '.') {
        className = mContext.getPackageName() + className;
    }
    // instanceFragment反射的细节,可以看看
    final Fragment frag = instantiateFragment(mContext, mFragmentManager,
            className, args);
    frag.setArguments(args);
    final FragmentTransaction ft = mFragmentManager.beginTransaction();

    // Fragment的进出 动画
    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);
    }

    // 然后把mContainerId,替换到ft,相当于把 我们的 第一个Fragment加入到了 UI上面了
    // 就是把 MainPage1Fragment 加入到 activity_main.xml 的 FragmentContainerView
    // mContainerId(就是 FragmentContainerView)相当于容器
    // frag(就是 我们的第一个Fragment实例 MainPage1Fragment)
    ft.replace(mContainerId, frag);
    ft.setPrimaryNavigationFragment(frag);

    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;
    if (initialNavigation) {
        isAdded = true;
    } else if (isSingleTopReplacement) {
        // Single Top means we only want one instance on the back stack
        if (mBackStack.size() > 1) {
            mFragmentManager.popBackStack(
                    generateBackStackName(mBackStack.size(), mBackStack.peekLast()),
                    FragmentManager.POP_BACK_STACK_INCLUSIVE);
            ft.addToBackStack(generateBackStackName(mBackStack.size(), destId));
        }
        isAdded = false;
    } else {
        ft.addToBackStack(generateBackStackName(mBackStack.size() + 1, destId));
        isAdded = true;
    }
    if (navigatorExtras instanceof Extras) {
        Extras extras = (Extras) navigatorExtras;
        for (Map.Entry sharedElement : extras.getSharedElements().entrySet()) {
            ft.addSharedElement(sharedElement.getKey(), sharedElement.getValue());
        }
    }
    ft.setReorderingAllowed(true);
    //提交任务,并执行fragment的生命周期
    ft.commit();
    // The commit succeeded, update our view of the world
    if (isAdded) {
        mBackStack.add(destId);
        return destination;
    } else {
        return null;
    }
}
  • 实例 fragment
@Deprecated
@NonNull
public Fragment instantiateFragment(@NonNull Context context,
        @NonNull FragmentManager fragmentManager,
        @NonNull String className, @SuppressWarnings("unused") @Nullable Bundle args) {
    // instantiate 反射的细节看看
    return fragmentManager.getFragmentFactory().instantiate(
            context.getClassLoader(), className);
}
  • FragmentFactory#instantiate的方法中通过反射获取 fragment
@NonNull
public Fragment instantiate(@NonNull ClassLoader classLoader, @NonNull String className) {
    try {
        Class cls = loadFragmentClass(classLoader, className);
        return cls.getConstructor().newInstance(); // 把最终方式的Fragment结果 给返回
    } catch (java.lang.InstantiationException e) {
        throw new Fragment.InstantiationException("Unable to instantiate fragment " + className
                + ": make sure class name exists, is public, and has an"
                + " empty constructor that is public", e);
    } catch (IllegalAccessException e) {
        throw new Fragment.InstantiationException("Unable to instantiate fragment " + className
                + ": make sure class name exists, is public, and has an"
                + " empty constructor that is public", e);
    } catch (NoSuchMethodException e) {
        throw new Fragment.InstantiationException("Unable to instantiate fragment " + className
                + ": could not find Fragment constructor", e);
    } catch (InvocationTargetException e) {
        throw new Fragment.InstantiationException("Unable to instantiate fragment " + className
                + ": calling Fragment constructor caused an exception", e);
    }
}

六、NavController 控制器

  • 设置资源
    public void setGraph(@NavigationRes int graphResId, @Nullable Bundle startDestinationArgs) {
        setGraph(getNavInflater().inflate(graphResId), startDestinationArgs);
    }

    public void setGraph(@NonNull NavGraph graph) {
        setGraph(graph, null);
    }

    public NavInflater getNavInflater() {
        if (mInflater == null) {
            //初始化 NavInflater
            mInflater = new NavInflater(mContext, mNavigatorProvider);
        }
        return mInflater;
    }
    // 可以看到 把 GraphID xml 里面的内容  转变成 NavGraph对象了
    public void setGraph(@NonNull NavGraph graph, @Nullable Bundle startDestinationArgs) {
        if (mGraph != null) {
            // 对象信息有了后,先从栈里面去弹,看能不能弹出来,第一次肯定是弹不出来的
            popBackStackInternal(mGraph.getId(), true);
        }
        // 先把对象 保存到成员
        mGraph = graph;
        // 此函数就能看到,如何把我们的默认Fragment UI给显示出来了
        onGraphCreated(startDestinationArgs);
    }

NavController#onGraphCreated

private void onGraphCreated(@Nullable Bundle startDestinationArgs) {
    if (mNavigatorStateToRestore != null) {
        ArrayList navigatorNames = mNavigatorStateToRestore.getStringArrayList(
                KEY_NAVIGATOR_STATE_NAMES);
        if (navigatorNames != null) {
            for (String name : navigatorNames) {
                Navigator navigator = mNavigatorProvider.getNavigator(name);
                Bundle bundle = mNavigatorStateToRestore.getBundle(name);
                if (bundle != null) {
                    navigator.onRestoreState(bundle);
                }
            }
        }
    }
    // 这里都是 栈状态的更新 保存 等处理,
    if (mBackStackToRestore != null) {
        for (Parcelable parcelable : mBackStackToRestore) {
            NavBackStackEntryState state = (NavBackStackEntryState) parcelable;
            NavDestination node = findDestination(state.getDestinationId());
            if (node == null) {
                final String dest = NavDestination.getDisplayName(mContext,
                        state.getDestinationId());
                throw new IllegalStateException("Restoring the Navigation back stack failed:"
                        + " destination " + dest
                        + " cannot be found from the current destination "
                        + getCurrentDestination());
            }
            Bundle args = state.getArgs();
            if (args != null) {
                args.setClassLoader(mContext.getClassLoader());
            }
            NavBackStackEntry entry = new NavBackStackEntry(mContext, node, args,
                    mLifecycleOwner, mViewModel,
                    state.getUUID(), state.getSavedState());
            mBackStack.add(entry);
        }
        updateOnBackPressedCallbackEnabled();
        mBackStackToRestore = null;
    }
    
    // 此代码就比较关键了,如果 前面辛辛苦苦保存的mGraph对象不为空,并且,栈里面是空的 取不出来的情况下, 
    if (mGraph != null && mBackStack.isEmpty()) {
        boolean deepLinked = !mDeepLinkHandled && mActivity != null
                && handleDeepLink(mActivity.getIntent());
        if (!deepLinked) {
             // 这里说的很清楚了,导航到图表中的第一个目的地
            navigate(mGraph, startDestinationArgs, null, null); 
        }
    } else {
        dispatchOnDestinationChanged();
    }
}

方法NavController#navigation是导航目的地

private void navigate(@NonNull NavDestination node, @Nullable Bundle args,
        @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
    boolean popped = false;
    boolean launchSingleTop = false;
    if (navOptions != null) {
        if (navOptions.getPopUpTo() != -1) {
            popped = popBackStackInternal(navOptions.getPopUpTo(),
                    navOptions.isPopUpToInclusive());
        }
    }
    // 同学们注意:这里是关键,真正的导航过程了
    Navigator navigator = mNavigatorProvider.getNavigator(node.getNavigatorName());
    // 导航需要的参数集
    Bundle finalArgs = node.addInDefaultArgs(args); 
    // 构建新的目的地对象,此对象,就是后续要完成的目标了
    NavDestination newDest = navigator.navigate(node, finalArgs,
            navOptions, navigatorExtras);
    if (newDest != null) {
        if (!(newDest instanceof FloatingWindow)) {
            while (!mBackStack.isEmpty()
                    && mBackStack.peekLast().getDestination() instanceof FloatingWindow
                    && popBackStackInternal(
                            mBackStack.peekLast().getDestination().getId(), true)) {
                // Keep popping
            }
        }

        ArrayDeque hierarchy = new ArrayDeque<>();
        NavDestination destination = newDest;
        if (node instanceof NavGraph) {
            do {
                NavGraph parent = destination.getParent();
                if (parent != null) {
                    NavBackStackEntry entry = new NavBackStackEntry(mContext, parent,
                            finalArgs, mLifecycleOwner, mViewModel);
                    hierarchy.addFirst(entry);
                    // Pop any orphaned copy of that navigation graph off the back stack
                    if (!mBackStack.isEmpty()
                            && mBackStack.getLast().getDestination() == parent) {
                        popBackStackInternal(parent.getId(), true);
                    }
                }
                destination = parent;
            } while (destination != null && destination != node);
        }

        // Now collect the set of all intermediate NavGraphs that need to be put onto
        // the back stack
        destination = hierarchy.isEmpty()
                ? newDest
                : hierarchy.getFirst().getDestination();
        while (destination != null && findDestination(destination.getId()) == null) {
            NavGraph parent = destination.getParent();
            if (parent != null) {
                NavBackStackEntry entry = new NavBackStackEntry(mContext, parent, finalArgs,
                        mLifecycleOwner, mViewModel);
                hierarchy.addFirst(entry);
            }
            destination = parent;
        }
        NavDestination overlappingDestination = hierarchy.isEmpty()
                ? newDest
                : hierarchy.getLast().getDestination();
        // Pop any orphaned navigation graphs that don't connect to the new destinations
        //noinspection StatementWithEmptyBody
        while (!mBackStack.isEmpty()
                && mBackStack.getLast().getDestination() instanceof NavGraph
                && ((NavGraph) mBackStack.getLast().getDestination()).findNode(
                        overlappingDestination.getId(), false) == null
                && popBackStackInternal(mBackStack.getLast().getDestination().getId(), true)) {
            // Keep popping
        }
        mBackStack.addAll(hierarchy);
        // The mGraph should always be on the back stack after you navigate()
        if (mBackStack.isEmpty() || mBackStack.getFirst().getDestination() != mGraph) {
            NavBackStackEntry entry = new NavBackStackEntry(mContext, mGraph, finalArgs,
                    mLifecycleOwner, mViewModel);
            mBackStack.addFirst(entry);
        }
        // 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(finalArgs);
        }
    }
    updateOnBackPressedCallbackEnabled();
    if (popped || newDest != null || launchSingleTop) {
        dispatchOnDestinationChanged();
    }
}

七、NavInflater 导航资源加载类

public final class NavInflater {
    private static final String TAG_ARGUMENT = "argument";
    private static final String TAG_DEEP_LINK = "deepLink";
    private static final String TAG_ACTION = "action";
    private static final String TAG_INCLUDE = "include";

    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public static final String APPLICATION_ID_PLACEHOLDER = "${applicationId}";

    private static final ThreadLocal sTmpValue = new ThreadLocal<>();

    private Context mContext;
    private NavigatorProvider mNavigatorProvider;

    public NavInflater(@NonNull Context context, @NonNull NavigatorProvider navigatorProvider) {
        mContext = context;
        mNavigatorProvider = navigatorProvider;
    }

    //解析资源 xml
    public NavGraph inflate(@NavigationRes int graphResId) {
        Resources res = mContext.getResources();
        //获取 xml 文件
        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();
        }
    }


    private NavDestination inflate(@NonNull Resources res, @NonNull XmlResourceParser parser,
            @NonNull AttributeSet attrs, int graphResId)
            throws XmlPullParserException, IOException {
        //获取导航
        Navigator navigator = mNavigatorProvider.getNavigator(parser.getName());
        //在 Navigator 的实现类中创建对应的导航目的
        final NavDestination dest = navigator.createDestination();
        //解析对应导航的资源
        dest.onInflate(mContext, attrs);

        final int innerDepth = parser.getDepth() + 1;
        int type;
        int depth;
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && ((depth = parser.getDepth()) >= innerDepth
                || type != XmlPullParser.END_TAG)) {
            if (type != XmlPullParser.START_TAG) {
                continue;
            }

            if (depth > innerDepth) {
                continue;
            }

            final String name = parser.getName();
            if (TAG_ARGUMENT.equals(name)) {
                inflateArgumentForDestination(res, dest, attrs, graphResId);
            } else if (TAG_DEEP_LINK.equals(name)) {
                inflateDeepLink(res, dest, attrs);
            } else if (TAG_ACTION.equals(name)) {
                inflateAction(res, dest, attrs, parser, graphResId);
            } else if (TAG_INCLUDE.equals(name) && dest instanceof NavGraph) {
                final TypedArray a = res.obtainAttributes(
                        attrs, androidx.navigation.R.styleable.NavInclude);
                final int id = a.getResourceId(
                        androidx.navigation.R.styleable.NavInclude_graph, 0);
                ((NavGraph) dest).addDestination(inflate(id));
                a.recycle();
            } else if (dest instanceof NavGraph) {
                ((NavGraph) dest).addDestination(inflate(res, parser, attrs, graphResId));
            }
        }

        return dest;
    }

}

执行NavGraph#addDestination保存导航

public class NavGraph extends NavDestination implements Iterable {
    //保存Destination
    final SparseArrayCompat mNodes = new SparseArrayCompat<>();
    private int mStartDestId;
    private String mStartDestIdName;

    public final void addDestinations(@NonNull Collection 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);
        }
    }

    public final void addDestination(@NonNull NavDestination node) {
        //获取节点 id
        int id = node.getId();
        if (id == 0) {
            throw new IllegalArgumentException("Destinations must have an id."
                    + " Call setId() or include an android:id in your navigation XML.");
        }
        if (id == getId()) {
            throw new IllegalArgumentException("Destination " + node + " cannot have the same id "
                    + "as graph " + this);
        }
        //根据 id 获取节点
        NavDestination existingDestination = mNodes.get(id);
        //同一个节点,直接返回
        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 NavDestination findNode(@IdRes int resid) {
        return findNode(resid, true);
    }

    //根据 id 查找节点
    final NavDestination findNode(@IdRes int resid, boolean searchParents) {
        NavDestination destination = mNodes.get(resid);
        // 返回对应的节点
        return destination != null
                ? destination
                : searchParents && getParent() != null ? getParent().findNode(resid) : null;
    }
}

你可能感兴趣的:(Navigation(一))