美文网首页
学习笔记--Fragment之FragmentTransacti

学习笔记--Fragment之FragmentTransacti

作者: RexHuang | 来源:发表于2020-09-23 11:48 被阅读0次
    FragmentTransaction

    Fragment的使用方式

    class TestActivity :AppCompatActivity(){
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_test)
            
            val firstFragment = FirstFragment()
            val secondFragment = SecondFragment()
            val thirdFragment = ThirdFragment()
    
            // 大部分的Fragment的使用方法都如下所示
            // 所以就从Fragment的基础使用开始入手分析
            supportFragmentManager
                .beginTransaction()
                .add(R.id.container, firstFragment)
                .replace(R.id.container,secondFragment)
                .remove(secondFragment)
                .remove(firstFragment)
                .add(R.id.container,thirdFragment)
                .addToBackStack(null)
                .commit()
        }
    }
    

    FragmentManager的来源

    跟踪AppCompatActivity的getSupportFragmentmanager()方法可以发现:

    //------------------------------FragmentActivity.java------------------------------
    
    public class FragmentActivity extends ComponentActivity implements
        ActivityCompat.OnRequestPermissionsResultCallback,
        ActivityCompat.RequestPermissionsRequestCodeValidator {
        
        ...
        
        // 赋值mFragments创建一个FragmentController实例,并传递HostCallbacks实例
        final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
        
        ...
        
        /**
         * Return the FragmentManager for interacting with fragments associated
         * with this activity.
         */
        @NonNull
        public FragmentManager getSupportFragmentManager() {
            // 直接委托给FragmentController实例对象mFragments的getSupportFragmentManager方法
            return mFragments.getSupportFragmentManager();
        }
        
        ...
        
        /**
         * Destroy all fragments.
         */
        @Override
        protected void onDestroy() {
            super.onDestroy();
             // 通过FragmentController调度Fragment的destroy生命周期
            mFragments.dispatchDestroy();
            mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
        }
        
        ...
        
        /**
         * Dispatch onPause() to fragments.
         */
        @Override
        protected void onPause() {
            super.onPause();
            mResumed = false;
            // 通过FragmentController调度Fragment的pause生命周期
            mFragments.dispatchPause();
            mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
        }
        
        ...
        
        /**
         * Dispatch onResume() to fragments.
         */
        @Override
        protected void onPostResume() {
            super.onPostResume();
            onResumeFragments();
        }
    
        /**
         * This is the fragment-orientated version of {@link #onResume()} that you
         * can override to perform operations in the Activity at the same point
         * where its fragments are resumed.  Be sure to always call through to
         * the super-class.
         */
        protected void onResumeFragments() {
            mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
            // 通过FragmentController调度Fragment的resume生命周期
            mFragments.dispatchResume();
        }
        
        ...
        
        /**
         * Dispatch onStart() to all fragments.
         */
        @Override
        protected void onStart() {
            super.onStart();
    
            mStopped = false;
    
            if (!mCreated) {
                mCreated = true;
                mFragments.dispatchActivityCreated();
            }
    
            mFragments.noteStateNotSaved();
            mFragments.execPendingActions();
    
            // NOTE: HC onStart goes here.
    
            mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
            // 通过FragmentController调度Fragment的resume生命周期
            mFragments.dispatchStart();
        }
        
        
        /**
         * Dispatch onStop() to all fragments.
         */
        @Override
        protected void onStop() {
            super.onStop();
    
            mStopped = true;
            markFragmentsCreated();
    
            // 通过FragmentController调度Fragment的resume生命周期
            mFragments.dispatchStop();
            mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
        }
        
        ...
        
        // HostCallbacks的继承自FragmentHostCallback,后面会介绍该类的用途
        class HostCallbacks extends FragmentHostCallback<FragmentActivity> implements
            ViewModelStoreOwner,
            OnBackPressedDispatcherOwner {
            
            ...        
        }
    }
    
    • 由于AppCompatActivity继承自FragmentActivity,并且没有覆写getSupportFragmentManager,所以追踪到
      FragmentActivity#getSupportFragmentManager中。
    • 可以看到FragmentActivity将getSupportFragmentManager直接委托给了FragmentController对象。而且不仅仅是getSupportFragmentManager的委托,在FragmentActivity的各个生命周期的方法中也是将Fragment的其它生命周期交由FragmentController进行调度

    接着看看FragmentController这个类

    FragmentController类
    //------------------------------FragmentController.java------------------------------
    
    public class FragmentController {
        private final FragmentHostCallback<?> mHost;
    
        /**
         * Returns a {@link FragmentController}.
         */
        @NonNull
        public static FragmentController createController(@NonNull FragmentHostCallback<?> callbacks) {
            return new FragmentController(checkNotNull(callbacks, "callbacks == null"));
        }
    
        private FragmentController(FragmentHostCallback<?> callbacks) {
            mHost = callbacks;
        }
    
        /**
         * Returns a {@link FragmentManager} for this controller.
         */
        // 将持有的FragmentHostCallback实例mHost的mFragmentManager返回
        @NonNull
        public FragmentManager getSupportFragmentManager() {
            return mHost.mFragmentManager;
        }
        
        ...
        
        /**
         * Moves all Fragments managed by the controller's FragmentManager
         * into the create state.
         * <p>Call when Fragments should be created.
         *
         * @see Fragment#onCreate(Bundle)
         */
        public void dispatchCreate() {
            mHost.mFragmentManager.dispatchCreate();
        }
    
        /**
         * Moves all Fragments managed by the controller's FragmentManager
         * into the activity created state.
         * <p>Call when Fragments should be informed their host has been created.
         *
         * @see Fragment#onActivityCreated(Bundle)
         */
        public void dispatchActivityCreated() {
            mHost.mFragmentManager.dispatchActivityCreated();
        }
    
        /**
         * Moves all Fragments managed by the controller's FragmentManager
         * into the start state.
         * <p>Call when Fragments should be started.
         *
         * @see Fragment#onStart()
         */
        public void dispatchStart() {
            mHost.mFragmentManager.dispatchStart();
        }
    
        /**
         * Moves all Fragments managed by the controller's FragmentManager
         * into the resume state.
         * <p>Call when Fragments should be resumed.
         *
         * @see Fragment#onResume()
         */
        public void dispatchResume() {
            mHost.mFragmentManager.dispatchResume();
        }
    
        /**
         * Moves all Fragments managed by the controller's FragmentManager
         * into the pause state.
         * <p>Call when Fragments should be paused.
         *
         * @see Fragment#onPause()
         */
        public void dispatchPause() {
            mHost.mFragmentManager.dispatchPause();
        }
    
        /**
         * Moves all Fragments managed by the controller's FragmentManager
         * into the stop state.
         * <p>Call when Fragments should be stopped.
         *
         * @see Fragment#onStop()
         */
        public void dispatchStop() {
            mHost.mFragmentManager.dispatchStop();
        }
    
        /**
         * @deprecated This functionality has been rolled into {@link #dispatchStop()}.
         */
        @Deprecated
        public void dispatchReallyStop() {
        }
    
        /**
         * Moves all Fragments managed by the controller's FragmentManager
         * into the destroy view state.
         * <p>Call when the Fragment's views should be destroyed.
         *
         * @see Fragment#onDestroyView()
         */
        public void dispatchDestroyView() {
            mHost.mFragmentManager.dispatchDestroyView();
        }
    
        /**
         * Moves Fragments managed by the controller's FragmentManager
         * into the destroy state.
         * <p>
         * If the {@link androidx.fragment.app.FragmentHostCallback} is an instance of {@link ViewModelStoreOwner},
         * then retained Fragments and any other non configuration state such as any
         * {@link androidx.lifecycle.ViewModel} attached to Fragments will only be destroyed if
         * {@link androidx.lifecycle.ViewModelStore#clear()} is called prior to this method.
         * <p>
         * Otherwise, the FragmentManager will look to see if the
         * {@link FragmentHostCallback host's} Context is an {@link Activity}
         * and if {@link Activity#isChangingConfigurations()} returns true. In only that case
         * will non configuration state be retained.
         * <p>Call when Fragments should be destroyed.
         *
         * @see Fragment#onDestroy()
         */
        public void dispatchDestroy() {
            mHost.mFragmentManager.dispatchDestroy();
        }
        
        ...
    }
    
    • 首先可以看到FragmentController的构造函数传递了FragmentHostCallback实例并赋值给mHost,而这个实例则是在FragmentActivity调用FragmentController#createController传递过来的。
    • 最终getSupportFragmentManager返回的就是mHost的mFragmentManager。
    • 生命周期的调度方法也是由mHost的mFragmentManager对象完成的。

    那么mHost到底是一个怎样的存在呢?

    HostCallbacks类

    HostCallbacks类是FragmentActivity的内部类,继承自FragmentHostCallback(在上文FragmentActivity的源码截取中有体现)。那来看看FragmentHostCallback

    //------------------------------FragmentHostCallback.java------------------------------
    
    /**
     * Integration points with the Fragment host.
     * <p>
     * Fragments may be hosted by any object; such as an {@link Activity}. In order to
     * host fragments, implement {@link FragmentHostCallback}, overriding the methods
     * applicable to the host.
     */
    // 官方注释:Fragment可以被如何对象持有,例如(Activity),为了成为Fragment的宿主,必须继承FragmentHostCallback。
    public abstract class FragmentHostCallback<E> extends FragmentContainer {
        ...
    }
    

    根据类头注释可知,FragmentHostCallback是官方为Fragments的宿主写的抽象类,而作为Fragment的宿主则必须继承该抽象类,并为其覆写其中方法。
    而继承自FragmentHostCallback的FragmentActivity$HostCallbacks,就是FragmentActivity可以成为Fragment宿主的原因所在。

    思考一个问题

    直接在FragmentActivity中使用FragmentManager也可以达到Fragment宿主的目的,为什么还要这样设计?

    在我们的认知中,FragmentActivity是专门用来管理Fragment的类,事实上FragmentActivity和Fragment没有进行任何关联甚至没有任何Fragment的导包,仅仅通过创建FragmentController实例并实现了FragmentHostCallback接口,就可以实现对Fragment的管理工作。这样的设计可以屏蔽宿主对Fragment的直接引用,从而拓展了Fragment的应用场景,而不仅仅局限于Activity中。如果未来需要出现一个新的宿主,就会显得十分方便。

    FragmentTransaction类

    通过supportManager.beginTransaction()就可以获得FragmentTransaction对象。FragmentTransaction是可以操作事务的对象,所谓的事务,就是可以一次性添加多个操作。比如Fragment使用方法中的add,replace,remove,remove,add可以组成是一个事务。也就是说一个事务可以存在多个操作。
    事务具有原子性,也就是说事务中的操作要么全部成功,要么全部不成功,不会出现部分成功的情况。

    追踪beginTransaction:

    //------------------------------FragmentManagerImpl.java------------------------------
    
    @NonNull
    @Override
    public FragmentTransaction beginTransaction() {
        // 返回一个新创建的BackStackRecord对象
        return new BackStackRecord(this);
    }
    

    可以发现返回的是一个BackStackRecord对象。BackStackRecord对象继续追踪

    //------------------------------BackStackRecord.java------------------------------
    
    /**
     * Entry of an operation on the fragment back stack.
     */
    // BackStackRecord继承自FragmentTransaction,是Fragment回退栈的一个元素
    final class BackStackRecord extends FragmentTransaction implements
        FragmentManager.BackStackEntry, FragmentManagerImpl.OpGenerator {
        
        static final String TAG = FragmentManagerImpl.TAG;
    
        final FragmentManagerImpl mManager;
        ...
        
        public BackStackRecord(FragmentManagerImpl manager) {
            mManager = manager;
        }
        
        ...
    

    BackStackRecord可以理解为Fragment回退栈的一个元素。由于继承自FragmentTransaction,BackStackRecord也可以理解成一个事务,这样设计的目的是在操作回退栈时,可以逆向执行事务,从而实现Fragment的出栈操作。

    FragmentTransaction的操作

    接下来我们分析下事务中如何进行Fragment的操作的,以supportFragmentManager.beginTransaction().add(R.id.container, firstFragment)的add操作为例

    //------------------------------FragmentTransaction.java------------------------------
    
    /**
     * Calls {@link #add(int, Fragment, String)} with a null tag.
     */
    @NonNull
    public FragmentTransaction add(@IdRes int containerViewId, @NonNull Fragment fragment) {
        // 直接调用doAddOp方法
        // 参数OP_ADD是一个int常量,代表的是本次操作是一个添加Fragment的操作
        doAddOp(containerViewId, fragment, null, OP_ADD);
        return this;
    }
    
    

    继续跟进doAddOp方法

    //------------------------------FragmentTransaction.java------------------------------
    void doAddOp(int containerViewId, Fragment fragment, @Nullable String tag, int opcmd) {
        final Class<?> fragmentClass = fragment.getClass();
        final int modifiers = fragmentClass.getModifiers();
        if (fragmentClass.isAnonymousClass() || !Modifier.isPublic(modifiers)
                || (fragmentClass.isMemberClass() && !Modifier.isStatic(modifiers))) {
            throw new IllegalStateException("Fragment " + fragmentClass.getCanonicalName()
                    + " must be a public static class to be  properly recreated from"
                    + " instance state.");
        }
    
        if (tag != null) {
            if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
                throw new IllegalStateException("Can't change tag of fragment "
                        + fragment + ": was " + fragment.mTag
                        + " now " + tag);
            }
            fragment.mTag = tag;
        }
    
        if (containerViewId != 0) {
            if (containerViewId == View.NO_ID) {
                throw new IllegalArgumentException("Can't add fragment "
                        + fragment + " with tag " + tag + " to container view with no id");
            }
            if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
                throw new IllegalStateException("Can't change container ID of fragment "
                        + fragment + ": was " + fragment.mFragmentId
                        + " now " + containerViewId);
            }
            fragment.mContainerId = fragment.mFragmentId = containerViewId;
        }
    
        // 将传递进来的opcmd以及fragment对象封装成一个Op(Operation)对象,进而调用addOp方法
        addOp(new Op(opcmd, fragment));
    }
    

    doAddOp方法进行了参数的校验,然后将传递进来的opcmd以及fragment对象封装成一个Op(Operation)对象,进而调用addOp方法

    //------------------------------FragmentTransaction.java------------------------------
    
    ArrayList<Op> mOps = new ArrayList<>();
    
    ...
    
    void addOp(Op op) {
        mOps.add(op);
        op.mEnterAnim = mEnterAnim;
        op.mExitAnim = mExitAnim;
        op.mPopEnterAnim = mPopEnterAnim;
        op.mPopExitAnim = mPopExitAnim;
    }
    

    可以看到addOp方法,仅仅是将op对象存储到集合mOps中,然后就结束了。为什么存储起来,因为一次事务有可能有多个操作,所以要存储起来,等到事务执行时再统一执行。

    接下来追踪replace的操作:

    //------------------------------FragmentTransaction.java------------------------------
    
    @NonNull
    public FragmentTransaction replace(@IdRes int containerViewId, @NonNull Fragment fragment,
            @Nullable String tag)  {
        if (containerViewId == 0) {
            throw new IllegalArgumentException("Must use non-zero containerViewId");
        }
        // 调用doAddOp方法
        // 参数OP_REPLACE是一个int常量,代表的是本次操作是一个替换Fragment的操作
        doAddOp(containerViewId, fragment, tag, OP_REPLACE);
        return this;
    }
    

    可以看到replace操作,也是经过doAddOp校验参数之后,会将opcmd和fragment组装成一个Op对象,同样加入mOps集合中。

    那也来看下remove操作

    //------------------------------FragmentTransaction.java------------------------------
    
    @NonNull
    public FragmentTransaction remove(@NonNull Fragment fragment) {
        addOp(new Op(OP_REMOVE, fragment));
    
        return this;
    }
    

    remove的操作,不需要经过校验参数,直接组装成Op对象,加入mOps集合中。

    FragmentTransaction的提交

    我们就以commit()为例,进行追踪

    //------------------------------FragmentTransaction.java------------------------------
    
    public abstract int commit();
    

    commit在FragmentTransaction是个抽象方法,而它的实现在BackStackRecord中

    //------------------------------BackStackRecord.java------------------------------
    
    @Override
    public int commit() {
        // 直接调用commitInternal方法,传递参数为false
        return commitInternal(false);
    }
    
    
    // 关注allowStateLoss参数,allowStateLoss的作用是当一条事务执行前会进行宿主状态的检查。
    // 如果宿主已经处于stop状态,再提交一条事务则会发生异常
    // 举一个例子:如果宿主因为内存不足被回收,而宿主已经执行了onSaveInstanceState,而此时又调用commit方法提交了事务,此时若allowStateLoss为false,就有可能触发异常了
    int commitInternal(boolean allowStateLoss) {
        // mCommitted是一个标志位,表示一个事务最多只能被提交一次,多次提交会抛出异常
        if (mCommitted) throw new IllegalStateException("commit already called");
        if (FragmentManagerImpl.DEBUG) {
            Log.v(TAG, "Commit: " + this);
            LogWriter logw = new LogWriter(TAG);
            PrintWriter pw = new PrintWriter(logw);
            dump("  ", pw);
            pw.close();
        }
        // 对标志位进行赋值
        mCommitted = true;
        if (mAddToBackStack) {
            mIndex = mManager.allocBackStackIndex(this);
        } else {
            mIndex = -1;
        }
        
        // 调用FragmentManager的enqueueAction方法,把本次操作和allowStateLoss参数传递过去
        mManager.enqueueAction(this, allowStateLoss);
        return mIndex;
    }
    
    //------------------------------BackStackRecord.java------------------------------
    
    
    /**
     * Adds an action to the queue of pending actions.
     *
     * @param action the action to add
     * @param allowStateLoss whether to allow loss of state information
     * @throws IllegalStateException if the activity has been destroyed
     */
    public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
        // 对allowStateLoss进行校验,如果allowStateLoss为false,则进入分支
        // allowStateLoss设计的目的是提醒开发者,宿主已经进行了onSaveInstance方法,当宿主因为内存不足等问题被回收,重新恢复时,新提交的事务是不能被恢复。以免在状态恢复时,让用户看到更奇怪的现象
        if (!allowStateLoss) {
            // 检查宿主的状态是否丢失,下面会追踪分析具体实现
            checkStateLoss();
        }
        synchronized (this) {
            if (mDestroyed || mHost == null) {
                if (allowStateLoss) {
                    // This FragmentManager isn't attached, so drop the entire transaction.
                    return;
                }
                throw new IllegalStateException("Activity has been destroyed");
            }
            if (mPendingActions == null) {
                mPendingActions = new ArrayList<>();
            }
            // 将action,也就是本次提交的事务添加到了一条等待队列中
            mPendingActions.add(action);
            // 然后执行scheduleCommit
            scheduleCommit();
        }
    }
    
    private void checkStateLoss() {
        // 如果宿主已经执行了onSaveInstanceState或者已经处于stop状态,则抛出异常
        if (isStateSaved()) {
            throw new IllegalStateException(
                    "Can not perform this action after onSaveInstanceState");
        }
    }
    
    @Override
    public boolean isStateSaved() {
        // See saveAllState() for the explanation of this.  We do this for
        // all platform versions, to keep our behavior more consistent between
        // them.
        // 返回的是宿主是否已经执行了onSaveInstanceState或者已经处于了stop的状态
        return mStateSaved || mStopped;
    }
    

    那么如何安全的提交事务呢?

    FragmentTransaction的4种提交方式
    • commit():如果在宿主执行了onSaveInstanceState之后再执行该操作,会抛出异常。属于异步操作(不是子线程操作,而是会将操作发送的主线程的轮询队列中,当主线程轮询到了才进行事务的操作。)
    • commitAllowStateLoss():如果在宿主执行了onSaveInstanceState之后再执行该操作,不会去检查宿主状态,不会抛出异常。但该操作不会被Activity记录,恢复时也就没办法恢复这些提交操作,所以该操作适用不重要的事务。同属于异步事务。
    • commitNow():会立刻执行当前提交的Transaction事务。属于同步事务。
    • commitNowAllowStateLoss():具备以上两者的特性,既是同步执行,也不会检查宿主的状态,有可能该操作不会被正确恢复。

    一般情况选择commitNowAllowStateLoss(),这样不会抛出异常中断用户的操作。

    Fragment页面重叠

    先来讲下页面的状态保存和恢复

    • onSaveInstanceState负责页面的状态保存。
    • onRestoreInstanceState以及onCreate方法会进行状态的恢复。

    onSaveInstanceStateonRestoreInstanceState并不一定会成对出现。
    页面状态保存(即onSaveInstanceState)的触发时机:

    • Home键回到桌面
    • 从最近列表跳转别的应用
    • 横竖屏切换
    • 内存不足时销毁重建

    Fragment页面重叠的主要原因是**FragmentActivity本身已经具有Fragment状态保存和状态恢复的功能。当Activity被回收并重建时,Fragment也会有相应的恢复操作,如果除了FragmentActivity自身自带的恢复操作之外,开发者在Activity的生命周期中也进行了Fragment的创建操作,则会造成重叠的效果 ** 。

    解决方法:

    • 在添加Fragment之前先判断FragmentManager中是否已经存在需要添加的Fragment(通过Tag)

    Fragment的懒加载

    在新的androidx的包下,官方推荐使用ViewPager2来替代ViewPager来实现懒加载。

    ViewPager2内部是根据RecyclerView来实现的,而且可以实现按需预加载。官方内置了ViewPager2组件的绑定适配器FragmentStateAdapter

    FragmentStateAdapter的作用是当页面切换时,可见的Fragment进入resume状态,不可见的都是进入onStart这个生命周期的状态,所以相当于实现了Fragment的懒加载。

    如果想继续使用ViewPager的话,官方也提供了新的适配器FragmentStatePagerAdapter,在创建FragmentStatePagerAdapter可以传递两种Behavior

        @Retention(RetentionPolicy.SOURCE)
        @IntDef({BEHAVIOR_SET_USER_VISIBLE_HINT, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT})
        private @interface Behavior { }
    
        /**
         * Indicates that {@link Fragment#setUserVisibleHint(boolean)} will be called when the current
         * fragment changes.
         *
         * @deprecated This behavior relies on the deprecated
         * {@link Fragment#setUserVisibleHint(boolean)} API. Use
         * {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT} to switch to its replacement,
         * {@link FragmentTransaction#setMaxLifecycle}.
         * @see #FragmentStatePagerAdapter(FragmentManager, int)
         */
        @Deprecated
        // 当ViewPager执行页面切换时,会执行setUserVisibleHint方法,这是向前兼容的
        public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0;
    
        /**
         * Indicates that only the current fragment will be in the {@link Lifecycle.State#RESUMED}
         * state. All other Fragments are capped at {@link Lifecycle.State#STARTED}.
         *
         * @see #FragmentStatePagerAdapter(FragmentManager, int)
         */
        // 当ViewPager执行页面切换时,只有可见的被选中的那个Fragment才会进入onResume状态,其它不可见的都会进入onStart
        public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1;
    

    实际上ViewPager2的FragmentStateAdapter和ViewPager的FragmentStatePagerAdapter是两个相似的功能,在页面切换的时候不会再去执行Fragment#setUserVisibleHint(boolean)方法了。

    相关文章

      网友评论

          本文标题:学习笔记--Fragment之FragmentTransacti

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