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

相关推荐
shenshizhong2 天前
Compose + Mvi 架构的玩android 项目,请尝鲜
android·架构·android jetpack
alexhilton6 天前
学会在Jetpack Compose中加载Lottie动画资源
android·kotlin·android jetpack
ljt27249606619 天前
Compose笔记(六十一)--SelectionContainer
android·笔记·android jetpack
QING6189 天前
Jetpack Compose 中的 ViewModel 作用域管理 —— 新手指南
android·kotlin·android jetpack
惟恋惜10 天前
Jetpack Compose 的状态使用之“界面状态”
android·android jetpack
喜熊的Btm10 天前
探索 Kotlin 的不可变集合库
kotlin·android jetpack
惟恋惜10 天前
Jetpack Compose 界面元素状态(UI Element State)详解
android·ui·android jetpack
惟恋惜10 天前
Jetpack Compose 多页面架构实战:从 Splash 到底部导航,每个 Tab 拥有独立 ViewModel
android·ui·架构·android jetpack
alexhilton12 天前
Jetpack Compose 2025年12月版本新增功能
android·kotlin·android jetpack