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 DataBinding源码解析与实践
android·android jetpack
alexhilton6 天前
Android技巧:学习使用GridLayout
android·kotlin·android jetpack
Wgllss14 天前
轻松搞定Android蓝牙打印机,双屏异显及副屏分辨率适配解决办法
android·架构·android jetpack
alexhilton20 天前
群星闪耀的大前端开发
android·kotlin·android jetpack
一航jason1 个月前
Android Jetpack Compose 现有Java老项目集成使用compose开发
android·java·android jetpack
帅次1 个月前
Android CoordinatorLayout:打造高效交互界面的利器
android·gradle·android studio·rxjava·android jetpack·androidx·appcompat
IAM四十二1 个月前
Jetpack Compose State 你用对了吗?
android·android jetpack·composer
Wgllss1 个月前
那些大厂架构师是怎样封装网络请求的?
android·架构·android jetpack
x0242 个月前
Android Room(SQLite) too many SQL variables异常
sqlite·安卓·android jetpack·1024程序员节
alexhilton2 个月前
深入理解观察者模式
android·kotlin·android jetpack