美文网首页Mobile
从源码角度看 Fragment 的启动流程及生命周期

从源码角度看 Fragment 的启动流程及生命周期

作者: Drew_MyINTYRE | 来源:发表于2022-02-25 11:39 被阅读0次

    本文基于 androidx fragment 1.2.2 源码分析

    implementation "androidx.fragment:fragment-ktx:1.2.2"
    

    请大家思考一个问题,我们知道 Fragment 的生命周期是与其宿主 Activity 的生命周期息息相关的,也即 Activity 的每次生命周期回调都会引发每个Fragment的类似回调,怎么实现的呢?

    因此,Fragment 中两个最重要的概念出现了,FragmentManagerFragmentTransaction(事务是指一组原子性的操作,这些操作是不可分割的整体,要么全完成,要么全不完成,完成后可以回滚到完成前的状态)。

    FragmentManager 封装着对 Fragment 操作的各种方法,addFragment(), removeFragment() 等等,而 FragmentActivity 通过 FragmentController 来操作 FragmentManager

    它们均为抽象类,需要具体的实现类。

    • FragmentManager 的实现类为 FragmentManagerImpl,其内部逻辑已全部移至 FragmentManager 中,是个空实现。

    • FragmentTransaction 的实现类为 BackStackRecord ,其内部引用了 FragmentManager 的实例 ,同时重写了父类的 四个 commit(...) 相关的方法。

    Activity 操作 Fragment 生命周期

    Activity 会在各个生命周期节点通过 FragmentController 间接调用 FragmentManager 中的各种 dispatch- 方法,进而影响 Fragment 的生命周期。

    // FragmentActivity.java
    final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
    
    //以下代码省略部分逻辑
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        mFragments.dispatchCreate();
    }
    
    @Override
    protected void onStart() {
        mFragments.dispatchStart();
    }
    
    //onResume 彻底执行完毕的回调
    @Override
    protected void onPostResume() {
        mFragments.dispatchResume();
    }
    @Override
    protected void onPause() {
        mFragments.dispatchPause();
    }
    
    @Override
    protected void onStop() {
        mFragments.dispatchStop();
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mFragments.dispatchDestroy();
    }
    

    这里引出 Fragment 中另外两个比较重要的概念,getParentFragmentManager()getChildFragmentManager()

    注意:requireFragmentManager()getFragmentManager 已弃用。

    • getChildFragmentManager() 获取的是 Fragment 中的 mChildFragmentManager;

    • getParentFragmentManager() 获取的是 Fragment 中的 mFragmentManager;

    • mChildFragmentManager 为 Fragment 内部的 FragmentManager

    // Private fragment manager for child fragments inside of this one.
    @NonNull
    FragmentManager mChildFragmentManager = new FragmentManagerImpl();
    

    getParentFragmentManager() 稍显复杂

    • 如果 Fragment 的直接宿主是 Activity ,则返回的是 Activity 中的getSupportFragmentManager() 返回的 FragmentManager

    • 如果 Fragment 的直接宿主是 Fragment,即该 Fragment 是一个子 Fragment,则返回的是其父 Fragment 的 getChildFragmentManager()
      所以嵌套 Fragment 的生命周期是 父 Fragment 在各个生命周期节点上通过 mChildFragmentManager 调用 dispatch- 以影响其子 Fragment 的生命周期

    下面的代码均来自 androidx.fragment.app.Fragment

    // The fragment manager we are associated with.  Set as soon as the
    // fragment is used in a transaction; cleared after it has been removed
    // from all transactions.
    FragmentManager mFragmentManager;
    
    // Private fragment manager for child fragments inside of this one.
    @NonNull
    FragmentManager mChildFragmentManager = new FragmentManagerImpl();
    
    // If this Fragment is contained in another Fragment, this is that container.
    Fragment mParentFragment;
    
    /**Return the FragmentManager for interacting with fragments associated with this fragment's activity. Note that this will be non-null slightly before getActivity(), during the time from when the fragment is placed in a FragmentTransaction until it is committed and attached to its activity.
    If this Fragment is a child of another Fragment, the FragmentManager returned here will be the parent's getChildFragmentManager().
    Deprecated
    This has been removed in favor of getParentFragmentManager() which throws an IllegalStateException if the FragmentManager is null. Check if isAdded() returns false to determine if the FragmentManager is null.
    See Also:
    getParentFragmentManager()**/
    @Nullable
    @Deprecated
    final public FragmentManager getFragmentManager() {
        return mFragmentManager;
    }
    
    @NonNull
    @Deprecated
    public final FragmentManager requireFragmentManager() {
        return getParentFragmentManager();
    }
    
    @NonNull
    public final FragmentManager getParentFragmentManager() {
        FragmentManager fragmentManager = mFragmentManager;
        if (fragmentManager == null) {
            throw new IllegalStateException(
                    "Fragment " + this + " not associated with a fragment manager.");
        }
        return fragmentManager;
    }
    
    @NonNull
    final public FragmentManager getChildFragmentManager() {
        if (mHost == null) {
            throw new IllegalStateException("Fragment " + this + " has not been attached yet.");
        }
        return mChildFragmentManager;
    }
    
    // Returns the parent Fragment containing this Fragment. If this Fragment is attached directly to an Activity, returns null.
    @Nullable
    final public Fragment getParentFragment() {
        return mParentFragment;
    }
    
    @NonNull
    public final Fragment requireParentFragment() {
        Fragment parentFragment = getParentFragment();
        if (parentFragment == null) {
            Context context = getContext();
            if (context == null) {
                throw new IllegalStateException("Fragment " + this + " is not attached to"
                        + " any Fragment or host");
            } else {
                throw new IllegalStateException("Fragment " + this + " is not a child Fragment, it"
                        + " is directly attached to " + getContext());
            }
        }
        return parentFragment;
    }
    

    总结

    在 Activity 的 Host Fragment 中替换 Fragment 利用 parentFragmentManager,Fragment 添加子 Fragment 利用 childFragmentManager

    # MainActivity 之 HostFragment
    
    parentFragmentManager.commit {
                    addToBackStack(null)
                    replace<SingleStackParentFragment>(R.id.content)
                }
    
    # SingleStackParentFragment
    
    childFragmentManager.commit {
                        val fragment =
                            SingleStackChildFragment.newInstance(
                                name(containerId),
                                1
                            )
                        replace(containerId, fragment, fragment.stableTag)
                    }
    

    我们做一个总结:Activity 和 Fragment 会在各个生命周期节点通过调用子Fragment 的 parentFragmentManager(或者说父 Fragment 的 childFragmentManager 和 Activity 的 supportFragmentManager )中的各种 dispatch- 方法以影响寄生的 Fragment 的生命周期,同时寄生的 Fragment 也拥有自己生命周期的调用链(从状态A转移至状态B)

    不得不说 Fragment 的很多 API 并不是很好用,从 androidx Fragment 的更新频率也可以看出。比如 Fragment 中的 View 和 Fragment 本身的生命周期是不一致的,存在 onDestroyView 但 Fragment 没有销毁的情况。

    相关文章

      网友评论

        本文标题:从源码角度看 Fragment 的启动流程及生命周期

        本文链接:https://www.haomeiwen.com/subject/okkxrrtx.html