JetPack源码分析之Navigation原理(四)

42 阅读3分钟

上一篇文章说了FragmentManager 为什么能处理回退事件,下面我们来介绍一下 Navigation 的回退事件

说到 Navigation 的回退事件,其实在梳理代码的过程还是比较痛苦的,并不是代码有多难,但是想要了解开发者的思路就比较难了,在这里我将我的思路说一下,

Navigation 将回退栈拆分成了2部分
1:NavController 的回退事件 2:FragmentManager 的回退事件

为什么这么说,先来看一段代码

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);
    // 这里是 Navigation 原理 二 中讲到的跳转的逻辑
    NavDestination newDest = navigator.navigate(node, finalArgs,
            navOptions, navigatorExtras);
    // 如果有新的目标页,那么就需要处理回退事件
    if (newDest != null) {
        //悬浮window ,这里不是主要逻辑
        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;
        // 一个 navigation 文件就对应着一个 NavGraph ,
        //如果我们跳转的是新的 navigation 文件,就需要将 NavGraph 包装一下,
        //加入 NavController 的回退栈,
        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) {
                        // 这个方法会被多次调用
                        //处理Fragment 回退事件
                        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();
        // 上面处理的是一级标签,这里就是处理的二级标签, 类似<fragment>标签
        // 将目标页条件到回退栈
        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();
    }
}

从上面的注释可以看出来,在跳转到新的 NavGraph 之后,就会将新的 NavGraph 添加进入 Navigation 的回退栈, 同时还会处理 Fragment 回退栈,Fragment 处理的方式如下

boolean popBackStackInternal(@IdRes int destinationId, boolean inclusive) {
    if (mBackStack.isEmpty()) {
        // Nothing to pop if the back stack is empty
        return false;
    }
    ArrayList<Navigator<?>> popOperations = new ArrayList<>();
    // 将栈倒过来遍历
    Iterator<NavBackStackEntry> iterator = mBackStack.descendingIterator();
    boolean foundDestination = false;
    while (iterator.hasNext()) {
        NavDestination destination = iterator.next().getDestination();
        Navigator<?> navigator = mNavigatorProvider.getNavigator(
                destination.getNavigatorName());
         //inclusive 为tue 或者 不是目标页,就添加进入需要回退
        if (inclusive || destination.getId() != destinationId) {
            popOperations.add(navigator);
        }
        // 找到目标停止
        if (destination.getId() == destinationId) {
            foundDestination = true;
            break;
        }
    }
    if (!foundDestination) {
        // We were passed a destinationId that doesn't exist on our back stack.
        // Better to ignore the popBackStack than accidentally popping the entire stack
        String destinationName = NavDestination.getDisplayName(mContext, destinationId);
        Log.i(TAG, "Ignoring popBackStack to destination " + destinationName
                + " as it was not found on the current back stack");
        return false;
    }
    boolean popped = false;
    for (Navigator<?> navigator : popOperations) {
        // 这里是回退Fragment 逻辑,会调用FragmentNavigator.popBackStack() 方法
        if (navigator.popBackStack()) {
            NavBackStackEntry entry = mBackStack.removeLast();
            if (entry.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.CREATED)) {
                entry.setMaxLifecycle(Lifecycle.State.DESTROYED);
            }
            if (mViewModel != null) {
                mViewModel.clear(entry.mId);
            }
            popped = true;
        } else {
            // The pop did not complete successfully, so stop immediately
            break;
        }
    }
    updateOnBackPressedCallbackEnabled();
    return popped;
}

FragmentNavigator.popBackStack回退方法就是调用FragmentManager 的回退事件

@Override
public boolean popBackStack() {
    if (mBackStack.isEmpty()) {
        return false;
    }
    if (mFragmentManager.isStateSaved()) {
        Log.i(TAG, "Ignoring popBackStack() call: FragmentManager has already"
                + " saved its state");
        return false;
    }
    mFragmentManager.popBackStack(
            generateBackStackName(mBackStack.size(), mBackStack.peekLast()),
            FragmentManager.POP_BACK_STACK_INCLUSIVE);
    mBackStack.removeLast();
    return true;
}

到了这里基本也梳理完了,但是还是有一些疑问,需要后续来慢慢分析