上一篇文章说了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;
}
到了这里基本也梳理完了,但是还是有一些疑问,需要后续来慢慢分析