前面2篇文章梳理了一下 NavHostFragment的整个初始化流程 与 跳转的过程,但是其中涉及到的回退栈的问题都有所跳过, 原因就是想要了解他的回退栈,就需要知道FragmentManager 回退栈的工作原理, 这样理解起来才能更加的深刻,下面我就将我自己了解到的回退栈相关的知识梳理一下
1:那么 FragmentManager 是怎么拿到导航里面的返回事件呢, 下面我们来梳理一下这个流程
FragmentActivity 继承自 ComponentActivity ,以下代码出自 ComponentActivity
less
@Override
@MainThread
public void onBackPressed() {
mOnBackPressedDispatcher.onBackPressed();
}
在调用 ComponentActivity onBackPressed 方法时,直接调用的是 他的 mOnBackPressedDispatcher 的方法
java
private final OnBackPressedDispatcher mOnBackPressedDispatcher =
new OnBackPressedDispatcher(new Runnable() {
@Override
public void run() {
// Calling onBackPressed() on an Activity with its state saved can cause an
// error on devices on API levels before 26. We catch that specific error and
// throw all others.
try {
ComponentActivity.super.onBackPressed();
} catch (IllegalStateException e) {
if (!TextUtils.equals(e.getMessage(),
"Can not perform this action after onSaveInstanceState")) {
throw e;
}
}
}
});
在创建 OnBackPressedDispatcher 的时候传入了一个Runnable ,这个 Runnable 就是为了在前面没有拦截的回退事件时调用的回退事件,也就是默认的super.OnBackPressed 事件,
接下来继续看看 OnBackPressedDispatcher 是如何分发事件的,也就是他的 onBackPressed 方法
scss
public void onBackPressed() {
// 返回一个反向的Iterator
Iterator<OnBackPressedCallback> iterator =
mOnBackPressedCallbacks.descendingIterator();
// 遍历 这些迭代器
while (iterator.hasNext()) {
OnBackPressedCallback callback = iterator.next();
// 如果这个callback是打开状态
if (callback.isEnabled()) {
// 让这个callback 执行back 事件
callback.handleOnBackPressed();
return;
}
}
// 如果所有的callback都没有拦截,则使用构建时的传入的Runnable,
// 也就是 ComponentActivity.super.onBackPressed();
if (mFallbackOnBackPressed != null) {
mFallbackOnBackPressed.run();
}
}
一个小例子,我们向 OnBackPressedDispatcher 顺序的添加 a b c 3个callback ,那么在 OnBackPressedDispatcher 的onBackPressed 方法里面, while 最先获得的是c 这个callback,如果c 是打开状态,那么b 与 a 是拿不到这个状态的,如果c 是关闭的状态,第二次获取则拿到的是b, 以此类推 最后一个是a,如果c b a 这3个callback都没有打开,则会执行他的默认的Runnable , 也就是 ComponentActivity.super.onBackPressed() 方法
从上面的逻辑可以判断出来,想要拦截 FragmentActivity 的回退事件,只需要向他的 mOnBackPressedDispatcher 中添加callback 即可,
2:SupportFragmentManager 为什么能处理导航里面的回退事件
想要搞清楚这个问题我们就需要知道 FragmentActivity 与 FragmentManager 的关系
FragmentActivity 中创建了一个 HostCallback,这个HostCallback 将FragmentActivity 与 FragmentManager 连接到一起
kotlin
HostCallback
@NonNull
@Override
public OnBackPressedDispatcher getOnBackPressedDispatcher() {
return FragmentActivity.this.getOnBackPressedDispatcher();
}
在 FragmentManager 的attachController 方法中,会获取到 这个 OnBackPressedDispatcher ,并且向里面添加callback ,并向里面添加自己的callback 具体方法如下
ini
if (host instanceof OnBackPressedDispatcherOwner) {
OnBackPressedDispatcherOwner dispatcherOwner = ((OnBackPressedDispatcherOwner) host);
mOnBackPressedDispatcher = dispatcherOwner.getOnBackPressedDispatcher();
LifecycleOwner owner = parent != null ? parent : dispatcherOwner;
mOnBackPressedDispatcher.addCallback(owner, mOnBackPressedCallback);
}
到了这里就可以解释为什么 FragmentManager 为什么可以管理回退栈了
3:SupportFragmentManager.setPrimaryNavigationFragment(this) 是如何工作的
在BackStackRecord 中的 executeOps 方法中会调用
ini
mManager.setPrimaryNavigationFragment(f);
这个方法 就会将Fragment 作为属性给到 FragmentManager ,具体方法如下
less
void setPrimaryNavigationFragment(@Nullable Fragment f) {
if (f != null && (!f.equals(findActiveFragment(f.mWho))
|| (f.mHost != null && f.mFragmentManager != this))) {
throw new IllegalArgumentException("Fragment " + f
+ " is not an active fragment of FragmentManager " + this);
}
Fragment previousPrimaryNav = mPrimaryNav;
mPrimaryNav = f;
dispatchParentPrimaryNavigationFragmentChanged(previousPrimaryNav);
dispatchParentPrimaryNavigationFragmentChanged(mPrimaryNav);
}
到了这里 Fragment 为什么能处理回退事件的基本原理就说完了,关于Navigation 的回退栈,我们下一篇文章分析