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

前面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 的回退栈,我们下一篇文章分析

相关推荐
Lei活在当下1 天前
【业务场景架构实战】4. 支付状态分层流转的设计和实现
架构·android jetpack·响应式设计
天花板之恋2 天前
Compose之图片加载显示
android jetpack
消失的旧时光-19432 天前
Kotlinx.serialization 使用讲解
android·数据结构·android jetpack
Tans53 天前
Androidx Fragment 源码阅读笔记(下)
android jetpack·源码阅读
Lei活在当下4 天前
【业务场景架构实战】2. 对聚合支付 SDK 的封装
架构·android jetpack
Tans56 天前
Androidx Fragment 源码阅读笔记(上)
android jetpack·源码阅读
alexhilton7 天前
runBlocking实践:哪里该使用,哪里不该用
android·kotlin·android jetpack
Tans510 天前
Androidx Lifecycle 源码阅读笔记
android·android jetpack·源码阅读
ljt272496066111 天前
Compose笔记(四十九)--SwipeToDismiss
android·笔记·android jetpack
4z3313 天前
Jetpack Compose重组优化:机制剖析与性能提升策略
性能优化·android jetpack