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

相关推荐
x02414 天前
Android Room(SQLite) too many SQL variables异常
sqlite·安卓·android jetpack·1024程序员节
alexhilton17 天前
深入理解观察者模式
android·kotlin·android jetpack
Wgllss17 天前
花式高阶:插件化之Dex文件的高阶用法,极少人知道的秘密
android·性能优化·android jetpack
上官阳阳20 天前
使用Compose创造有趣的动画:使用Compose共享元素
android·android jetpack
沐言人生24 天前
Android10 Framework—Init进程-15.属性变化控制Service
android·android studio·android jetpack
IAM四十二1 个月前
Android Jetpack Core
android·android studio·android jetpack
王能1 个月前
Kotlin真·全平台——Kotlin Compose Multiplatform Mobile(kotlin跨平台方案、KMP、KMM)
android·ios·kotlin·web·android jetpack·kmp·kmm
alexhilton1 个月前
让Activity更加优雅地跳转
android·kotlin·android jetpack
沐言人生1 个月前
Android10 Framework—Init进程-11.客户端操作属性
android·android studio·android jetpack
沐言人生1 个月前
Android10 Framework—Init进程-9.服务端属性值初始化
android·android studio·android jetpack