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

相关推荐
普通网友14 天前
Android-Jetpack架构组件(一)带你了解Android-Jetpack
jvm·架构·android jetpack
2401_8979156514 天前
Android Jetpack 之 Paging3的一些踩坑记录
android·android jetpack
我命由我1234515 天前
Android 项目依赖冲突问题:Duplicate class found in modules
android·xml·java·java-ee·android studio·android jetpack·android-studio
我命由我1234516 天前
11-3.Android 项目结构 - 认识 .idea 目录
android·xml·java·java-ee·gitee·android jetpack·android runtime
我命由我1234517 天前
11-2.Android 项目结构 - themes.xml 文件基础解读
android·xml·java·java-ee·gitee·android jetpack·android runtime
alexhilton18 天前
Compose多平台 (CMP) 开发的四个实用技巧
android·kotlin·android jetpack
我命由我1234520 天前
Android Room 构建问题:There are multiple good constructors
android·开发语言·java-ee·android studio·android jetpack·android-studio·android runtime
砖厂小工21 天前
Now In Android 精讲 3 - 眼花缭乱 Kotlin 类型
android·kotlin·android jetpack
工程师老罗1 个月前
我用Ai学Android Jetpack Compose之Button
android·android jetpack
工程师老罗1 个月前
我用AI学Android Jetpack Compose之Jetpack Compose学习路径篇
android·学习·android jetpack