学习笔记--Fragment之FragmentTransaction事务执行流程

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 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.
     * 

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. *

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. *

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. *

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. *

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. *

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. *

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. *

* 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. *

* 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. *

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.
 * 

* 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 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 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之FragmentTransaction事务执行流程)