Jetpack之Navigation技术解密

news2024/11/22 19:14:38

Navigation是什么

官方的话:

Navigation 是一个框架,用于在 Android 应用中的“目标”之间导航,该框架提供一致的 API,无论目标是作为 Fragment、Activity 还是其他组件实现。

自己的话:

Navigation是管理Fragment之间导航的组件库,特别在实现单个Activity多个Fragment的管理模式更加灵活

Navigation应用篇

在这里插入图片描述
1.Navigation Graph。这是一种新型的XML资源文件,其中包含应用程序所有的页面,以及页面间的关系。
2.NavHostFragment。这是一个特殊的Fragment,你可以认为它是其他Fragment的“容器”,Navigation Graph中的Fragment正是通过NavHostFragment进行展示的。3.NavController。这是一个Java/Kotlin对象,用于在代码中完成Navigation Graph中具体的页面切换工作。
当你想切换Fragment时,使用NavController对象,告诉它你想要去Navigation Graph中的哪个Fragment,NavController会将你想去的Fragment展示在NavHostFragment中。
具体的使用可以参考:https://blog.csdn.net/Lbsssss/article/details/121660838
这里就直接略过了。

Navigation源码原理篇

1.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;
}

2.NavHostFragment.onInflate

@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; // 就把此属性解析出来,activity_main.xml 的 app:navGraph="@navigation/nav_graph_main"

    }
    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; // 就把此属性解析出来,activity_main.xml 的 app:defaultNavHost="true"
    }
    a.recycle();
}

3.NavHostFragment.onCreateNavController

@CallSuper
protected void onCreateNavController(@NonNull NavController navController) {
    // 这里是通过导航暴露者,获取到 NavigationProvider 对象
    navController.getNavigatorProvider().addNavigator(
            new DialogFragmentNavigator(requireContext(), getChildFragmentManager()));
    navController.getNavigatorProvider().addNavigator(createFragmentNavigator());
}
public class NavigatorProvider {
    // 把导航页面,例如 三个Fragment保存  与  key保存 记录
    private static final HashMap<Class<?>, String> sAnnotationNames = new HashMap<>();
}
protected Navigator<? extends FragmentNavigator.Destination> createFragmentNavigator() {
    return new FragmentNavigator(requireContext(), getChildFragmentManager(),
            getContainerId()); // 此ID,如果不写xml文件,单纯用代码实现的时候,需要得到一个父容器ID
}

4.NavHostFragment.onCreateNav

@CallSuper
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    final Context context = requireContext();

    mNavController = new NavHostController(context); // 此处 实例化出 NavHostController
    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()
        mNavController.setGraph(mGraphId); // 设置GraphID,此处意义重大,会获取nav_graph_main里面的action等导航信息
    } 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);
}

5.NavController.setGraph

@CallSuper
public void setGraph(@NavigationRes int graphResId) {
    setGraph(graphResId, null);
}

@CallSuper
public void setGraph(@NavigationRes int graphResId, @Nullable Bundle startDestinationArgs) {
   // getNavInflater().inflate(graphResId) 开始真正的解析 nav_graph_main里面的action等导航信息,还要确定startDestination的Fragment
   // startDestinationArgs 代表要启动 app:startDestination="@id/page1Fragment" 的参数信息
    setGraph(getNavInflater().inflate(graphResId), startDestinationArgs);
}
@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();
    }
}
// 退会后,看到 setGraph 细节
@CallSuper // 可以看到 把 GraphID xml 里面的内容  转变成 NavGraph对象了
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); // 此函数就能看到,如何把我们的默认Fragment UI给显示出来了
}

6.NavController.onGraphCreated

private void onGraphCreated(@Nullable Bundle startDestinationArgs) {
    // 这个 这种 if的 ,对我们的主线流程意义不大,先跳过
    if (mNavigatorStateToRestore != null) {
        ArrayList<String> 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 to the first destination in the graph
            // if we haven't deep linked to a destination
            navigate(mGraph, startDestinationArgs, null, null); // 看此处是如何导航的
        }
    } else {
        dispatchOnDestinationChanged();
    }
}

7. 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<NavDestination> 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)) {
            // We've successfully navigating to the new destination, which means
            // we should pop any FloatingWindow destination off the back stack
            // before updating the back stack with our new destination
            //noinspection StatementWithEmptyBody
            while (!mBackStack.isEmpty()
                    && mBackStack.peekLast().getDestination() instanceof FloatingWindow
                    && popBackStackInternal(
                            mBackStack.peekLast().getDestination().getId(), true)) {
                // Keep popping
            }
        }

        // When you navigate() to a NavGraph, we need to ensure that a new instance
        // is always created vs reusing an existing copy of that destination
        ArrayDeque<NavBackStackEntry> 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();
    }
}

8.Navigator.navigate

@Nullable
public abstract NavDestination navigate(@NonNull D destination, @Nullable Bundle args,
        @Nullable NavOptions navOptions, @Nullable Extras navigatorExtras);

在这里插入图片描述

9.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) {
            // 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 {
        ft.addToBackStack(generateBackStackName(mBackStack.size() + 1, destId));
        isAdded = true;
    }
    if (navigatorExtras instanceof Extras) {
        Extras extras = (Extras) navigatorExtras;
        for (Map.Entry<View, String> sharedElement : extras.getSharedElements().entrySet()) {
            ft.addSharedElement(sharedElement.getKey(), sharedElement.getValue());
        }
    }
    ft.setReorderingAllowed(true);
    ft.commit();
    // The commit succeeded, update our view of the world
    if (isAdded) {
        mBackStack.add(destId);
        return destination;
    } else {
        return null;
    }
}
@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);
}
@NonNull
public Fragment instantiate(@NonNull ClassLoader classLoader, @NonNull String className) {
    try {
        Class<? extends Fragment> 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);
    }
}

注意:但onCreate执行完成后,界面就展示了第一个Fragment的画面了

10.小结

第一步:解析所有的目的地
第二步:放到一个HashMap里面全部保存好
第三步:获取第一个目的地Fragment
第四步:初始化目的地的所有信息
第五步:显示第一个目的地Fragment 画面

11.NavHostFragment.onViwCreated

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

navigation角度上分析源码

1. MainPage1Fragment

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    val btn = view.findViewById<Button>(R.id.btn)
    btn.setOnClickListener { view ->
        Navigation.findNavController(view).navigate(R.id.action_page2) // 在这里开始导航
    }
}

2.NavController.navigation

public void navigate(@IdRes int resId) {
    navigate(resId, null);
}

public void navigate(@IdRes int resId, @Nullable Bundle args) {
    navigate(resId, args, null);
}

public void navigate(@IdRes int resId, @Nullable Bundle args,
        @Nullable NavOptions navOptions) {
    navigate(resId, args, navOptions, null);
}

public void navigate(@IdRes int resId, @Nullable Bundle args, @Nullable NavOptions navOptions,
        @Nullable Navigator.Extras navigatorExtras) {
    // 目的 是在 Graph里面去寻找 当前节点的Fragment 为 目的地
    NavDestination currentNode = mBackStack.isEmpty() // 先去看看栈有没有
            ? mGraph
            : mBackStack.getLast().getDestination(); // 如果栈没有,就取最后一个
    if (currentNode == null) {
        throw new IllegalStateException("no current navigation node");
    }
    @IdRes int destId = resId;
    // 获取节点后,就要去获取 action 导航动作,只有action才能通过resId 获取下一个要导航的目标id
    final NavAction navAction = currentNode.getAction(resId); 
    Bundle combinedArgs = null;
    if (navAction != null) {
        if (navOptions == null) {
            navOptions = navAction.getNavOptions();
        }
        destId = navAction.getDestinationId(); // 获取下一个目的地的 id号信息等
        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);
    }

    if (destId == 0 && navOptions != null && navOptions.getPopUpTo() != -1) {
        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);
        if (navAction != null) {
            throw new IllegalArgumentException("Navigation destination " + dest
                    + " referenced from action "
                    + NavDestination.getDisplayName(mContext, resId)
                    + " cannot be found from the current destination " + currentNode);
        } else {
            throw new IllegalArgumentException("Navigation action/destination " + dest
                    + " cannot be found from the current destination " + currentNode);
        }
    }
    // 这行代码就是我们要关心的主线流程
    navigate(node, combinedArgs, navOptions, navigatorExtras);
}

3.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<NavDestination> navigator = mNavigatorProvider.getNavigator(
            node.getNavigatorName());
    Bundle finalArgs = node.addInDefaultArgs(args);
    // 同学们 还记得 新的目的地么? OK 分析完毕了,无效重复分析了
    NavDestination newDest = navigator.navigate(node, finalArgs,
            navOptions, navigatorExtras);

    if (newDest != null) {
        if (!(newDest instanceof FloatingWindow)) {
            // We've successfully navigating to the new destination, which means
            // we should pop any FloatingWindow destination off the back stack
            // before updating the back stack with our new destination
            //noinspection StatementWithEmptyBody
            while (!mBackStack.isEmpty()
                    && mBackStack.peekLast().getDestination() instanceof FloatingWindow
                    && popBackStackInternal(
                            mBackStack.peekLast().getDestination().getId(), true)) {
                // Keep popping
            }
        }

        // When you navigate() to a NavGraph, we need to ensure that a new instance
        // is always created vs reusing an existing copy of that destination
        ArrayDeque<NavBackStackEntry> 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();
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
1.官网上:MainActivity去绑定Navigation (实际上:源码理解 Fragment帖到MainActivity)

2.NavHostFragment create 实例化NavHostFragment 并且 赋予丰富的信息而已

3.NavHostFragment.onInflate属于Fragment的生命周期 { app:defaultNavHost=“true”
app:navGraph=“@navigation/nav_graph_main” }

4.protected void onCreateNavController(@NonNull NavController navController) {
NavigatorProvider HashMap会去保存所有的Fragment信息 3个
getContainerId() 不写xml的时候,写Java代码的是才有用
}

5.onCreate
mContainerId
<androidx.fragment.app.FragmentContainerView
android:name=“androidx.navigation.fragment.NavHostFragment”>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/519223.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

win11、VS2019下配置PCL1.11.1

1、PCL安装配置 下载pcl-1.11.1-pdb-msvc2019-win64与PCL-1.11.1-AllInOne-msvc2019-win64.exe文件。以管理员身份运行PCL-1.11.1-AllInOne-msvc2019-win64.exe程序&#xff0c;截图如下&#xff1a; 安装过程中没有弹出OpenNI2的安装&#xff0c;但是要安装在3rdParty下&#…

MySQL空间查询

MySQL空间查询 文章目录 MySQL空间查询1. 空间数据支持2. 空间函数支持3. 操作示例3.1 创建表结构3.2 插入数据3.3 查询空间相交数据 4. 参考链接 1. 空间数据支持 MySQL 具有对应于 OpenGIS 类的空间数据类型。一些空间数据类型包含单个几何值&#xff1a; GEOMETRYPOINTLIN…

PCS-2022-VVC中帧内和帧间预测的统一快速划分算法

本文来自PCS 2022的论文《Unified Fast Partitioning Algorithm for Intra and Inter Predictions in Versatile Video Coding》 介绍 VVC (Versatile Video Coding) 标准采用了比HEVC (High Efficiency Video Coding) 标准更灵活的划分结构&#xff0c;在HEVC的四叉树划分结构…

前端CSS经典面试题总结

前端CSS经典面试题总结 2.1 介绍一 下 CSS 的盒子模型&#xff1f;2.2 css 选择器优先级&#xff1f;2.3 垂直居中几种方式&#xff1f;2.4 简明说一下 CSS link 与 import 的区别和用法&#xff1f;2.5 rgba和opacity的透明效果有什么不同&#xff1f;2.6 display:none和visib…

在大模型的喧嚣中,SAS向企业AI押注70亿

ChatGPT和GPT所代表的大模型&#xff0c;已经在国内形成了海啸效应&#xff0c;几乎所有顶级科技企业都在想方设法进入大模型赛道。大模型的最大价值在于普遍提升个人生产力&#xff0c;而各行各业的公司都在积极寻找应用大模型和生成式AI的机会&#xff0c;以普遍提升全员生产…

IntelliJ IDEA 安装及配置详细教程

IDEAIntelliJ IDEA 安装及配置详细教程 1、下载2、安装3、IDEA使用设置3.1 进入设置界面3.2 JDK配置3.3 主题样式设置3.4 字体样式设置3.5 编辑器背景颜色设置3.6 字符集和配置文件编码格式设置3.7 设置鼠标悬浮提示 4、idea配置maven5、idea创建springboot工程 IntelliJ IDEA …

MYSQL02高级_目录结构、默认数据库、表文件、系统独立表空间

文章目录 ①. MySQL目录结构②. 查看默认数据库③. MYSQL5.7和8表文件③. 系统、独立表空间 ①. MySQL目录结构 ①. 如何查看关联mysql目录 [rootmysql8 ~]# find / -name mysql /var/lib/mysql /var/lib/mysql/mysql /etc/selinux/targeted/tmp/modules/100/mysql /etc/seli…

服务(第二十篇)mysql高级查询语句(一)

准备环境&#xff1a; 两张表&#xff1a;location和store_info&#xff1b; 1、查询语句类型 ①SELECT "字段" FROM "表名"&#xff1b; 只查看表中的指定字段&#xff1b; 还可以根据查询的字段位置进行排序&#xff1b; ②SELECT DISTINCT "字段…

C语言生成随机数【简易抽卡代码为例】

文章目录 前言一、生成随机数rand() 函数srand()函数time()函数生成一个真正的随机数 二、使用小技巧三、使用代码实例&#xff08;简易抽卡&#xff09;总结 前言 本文将详细解释如何在C语言中生成随机数&#xff0c;并介绍应用的小技巧 一、生成随机数 c语言生成一个真正的…

ESP32 partition(分区表)(15)

提示&#xff1a;本博客作为学习笔记&#xff0c;有错误的地方希望指正&#xff0c;主要参考乐鑫技术手册说明结合实例代码分析&#xff0c;结合理论知识学习后示例分析以及常见问题说明。 文章目录 一、ESP32 Partition概述二、内置分区表三、创建自定义分区表四、生成二进制分…

设备树(属性)简介

1 设备树 简单的整理记录&#xff1b; 学习参考内容&#xff1a; Linux 笔记 https://xuesong.blog.csdn.net/article/details/109522945?spm1001.2014.3001.5502正点原子-左盟主 驱动开发网络资料&#xff1a;IT界小生 https://www.zhihu.com/column/itlife 1.1 设备树简介…

springboot+freemarker+restful

什么是freemarker? FreeMarker是一种模板引擎&#xff0c;它可以用于生成各种类型的文档&#xff0c;比如HTML、XML、PDF、Word等。它可以通过简单的模板语法和数据模型来生成文档内容。与传统的JSP相比&#xff0c;FreeMarker的语法更加简洁和易读&#xff0c;并且可以很好地…

女生学习软件测试怎么样?

在IT技术行业&#xff0c;女生学习还是有很大优势的。女生相较于男生更有耐心&#xff0c;包容性强&#xff0c;心思细腻&#xff0c;对细节把控更好&#xff0c;同时还能帮助团队男女平衡&#xff0c;活跃气氛。 编程是一个只要你肯学习就会有回报的行业&#xff0c;不论男生…

1W字理解Java虚拟机——JVM

目录 一、初识JVM 二、JVM执行流程 三、内存区域划分&#xff08;JVM运行时数据区&#xff09; 3.1 本地方法栈&#xff08;线程私有&#xff09; 3.2 程序计数器&#xff08;线程私有&#xff0c;无并发问题&#xff09; 3.3 JVM虚拟机栈&#xff08;线程私有&#xff0…

【小程序】微信云托管对象存储管理

微信云托管对象存储用于存放数据或文件&#xff0c;一般用于较大数据或较大文件上传时的中转对象&#xff0c;避免直接上传到服务端&#xff0c;影响服务性能。 对象存储 开通了云托管平台后会自动开通对象存储功能。简单理解就是一个文件目录即可。 存储文件 文件名称&…

串口全双工通信与串口中断

1.串口通信编程 STC-ISP串口助手的使用&#xff1a; 文本模式和HEX模式的区别&#xff1a;文本模式就是那些可打印的字符。HEX模式就是这些可打印字符对应的16进制。它们都对应相同的ASCII码&#xff08;用十进制表示&#xff09;。 很多小白在程序编写完成后调试时会搞不清楚…

利用notepad++处理数据,再用excel做则线图

1、利用串口调试XCOM V2.8得到数据 2、利用Notepad编辑数据 利用正则表达式 删除时间戳 移除空行 继续删掉不要的数据 3、用excel生成折线图 复制数据到excel excel自动根据上文公式填充计算 输入0.1和0.2 框选0.1和0.2&#xff0c;下拉

JavaScript-jQuery的使用 + JS的案例

目录 点击更换图片 猜数字 搜索页面展示 表白墙 点击更换图片 我们先看下面这个例子: 使用input里面的button按钮, 并且利用函数, 将一个搜狗logo转换为百度logo: <!DOCTYPE html> <html lang"en"> <head><meta charset&…

Maven下载安装及IDEA配置Maven的超详细教程

Maven下载安装及IDEA配置Maven的超详细教程 1、IntelliJ IDEA 下载、安装及配置过程2、maven下载、安装、配置过程2.1 mavan下载2.2 安装2.3 配置 3、在IDEA中配置Maven3.1 进入设置界面3.2 maven配置 4、IDEAmaven创建工程示例 Maven是一个能使我们的java程序开发节省时间和精…

ssl证书过期

SSL证书验证网站所有者的身份&#xff0c;并为其访问者建立与服务器的安全加密连接。它保护他们的安全和隐私。 但SSL证书并非永远有效。与您的驾驶执照或护照一样&#xff0c;SSL证书也有过期日期。过期日期后&#xff0c;服务器的身份不再受信任。 为什么网站安全证书会过期…