AndroidX Fragment探究-事务操作

概述

平时开发中经常使用Fragment的场景是创建Fragment并添加到FragmentActivity的指定布局容器中。要实现这样的操作,首先需要获取FragmentManager,接着开启事务FragmentTransaction,并添加add、remove、replace、hide、show等等操作,最后commitXXX提交事务执行对应操作。

接下来进入源码追踪这个过程,看看FragmentManager是如何进行调度执行。

源码探究

文中源码基于’androidx.fragment:fragment:1.1.0’

FragmentManager的由来

在FragmentActivity中通过getSupportFragmentManager方法来获取FragmentManager:

[FragmentActivity.java]

public FragmentManager getSupportFragmentManager() {
   
    return mFragments.getSupportFragmentManager();
}

该方法中又通过mFragments成员来获取:
[FragmentController.java]

public FragmentManager getSupportFragmentManager() {
   
    return mHost.mFragmentManager;
}

其中又通过mHost成员来获取。

在研究FragmentManager之前,先来看看mFragments、mHost是什么。

FragmentController

先来看看FragmentActivity的mFragments成员:

[FragmentActivity.java]

public class FragmentActivity extends ComponentActivity implements
        ActivityCompat.OnRequestPermissionsResultCallback,
        ActivityCompat.RequestPermissionsRequestCodeValidator {
   
    // ···
    final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
    // ···
}

mFragments为FragmentController对象。

接着看createController方法,这里传入HostCallbacks实例:
[FragmentController.java]

public static FragmentController createController(@NonNull FragmentHostCallback<?> callbacks) {
   
    return new FragmentController(checkNotNull(callbacks, "callbacks == null"));
}

private FragmentController(FragmentHostCallback<?> callbacks) {
   
    // 持有HostCallbacks引用
    mHost = callbacks;
}
HostCallbacks

在创建FragmentController时,实例化了HostCallbacks并赋值给FragmentController的mHost成员。

[FragmentActivity.java]

class HostCallbacks extends FragmentHostCallback<FragmentActivity> implements
        ViewModelStoreOwner,
        OnBackPressedDispatcherOwner {
   
    public HostCallbacks() {
   
        super(FragmentActivity.this /*fragmentActivity*/);
    }
    // ···
}

HostCallbacks继承自FragmentHostCallback,为FragmentActivity的内部类,持有FragmentActivity的引用。

[FragmentHostCallback.java]

public abstract class FragmentHostCallback<E> extends FragmentContainer {
   
    // ···
    FragmentHostCallback(@NonNull FragmentActivity activity) {
   
        this(activity, activity /*context*/, new Handler(), 0 /*windowAnimations*/);
    }

    FragmentHostCallback(@Nullable Activity activity, @NonNull Context context,
            @NonNull Handler handler, int windowAnimations) {
   
        // 持有FragmentActivity上下文引用
        mActivity = activity;
        mContext = Preconditions.checkNotNull(context, "context == null");
        // 构造函数中创建的Handler,默认运行在主线程
        mHandler = Preconditions.checkNotNull(handler, "handler == null");
        // 动画相关,默认为0
        mWindowAnimations = windowAnimations;
    }
    // ···
}

可以看出HostCallbacks持有当前FragmentActivity上下文,并创建了一个主线程Handler。

FragmentManagerImpl

FragmentManager为抽象类,而FragmentManagerImpl是其实现类:

[FragmentManagerImpl.java]

final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 {
   
    // ···
}

在FragmentHostCallback实例化时会创建FragmentManagerImpl:
[FragmentHostCallback.java]

public abstract class FragmentHostCallback<E> extends FragmentContainer {
   
    // ···
    final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
    // ···
}

FragmentHostCallback的mFragmentManager成员持有FragmentManagerImpl。

FragmentManagerImpl的绑定

在FragmentActivity的onCreate方法中:
[FragmentActivity.java]

protected void onCreate(@Nullable Bundle savedInstanceState) {
   
    // 调用FragmentController的attachHost方法,参数传null
    mFragments.attachHost(null /*parent*/);
    // ···
}

接着看attachHost方法:
[FragmentController.java]

public void attachHost(@Nullable Fragment parent) {
   
    // 调用FragmentManagerImpl的attachController方法,并传入HostCallbacks实例,参数parent为null。
    mHost.mFragmentManager.attachController(
            mHost, mHost /*container*/, parent);
}

进入FragmentManagerImpl的attachController方法:
[FragmentManagerImpl.java]

public void attachController(@NonNull FragmentHostCallback host,
        @NonNull FragmentContainer container, @Nullable final Fragment parent) {
   
    if (mHost != null) throw new IllegalStateException("Already attached");
    // 持有HostCallbacks引用
    mHost = host;
    mContainer = container;
    mParent = parent;
    if (mParent != null) {
   
        // Since the callback depends on us being the primary navigation fragment,
        // update our callback now that we have a parent so that we have the correct
        // state by default
        updateOnBackPressedCallbackEnabled();
    }
    // Set up the OnBackPressedCallback
    // 省略BackPressed设置部分
    // ···
    
    // Get the FragmentManagerViewModel
    // 省略ViewModel设置部分
    // ···
}

FragmentActivity在onCreate中进行FragmentManagerImpl的绑定操作,在FragmentManagerImpl的attachController方法中接收HostCallbacks实例并保存。

FragmentController、HostCallbacks、FragmentManagerImpl之间关系

FragmentController类图

FragmentActivity持有FragmentController引用,FragmentController持有HostCallbacks引用,HostCallbacks持有FragmentActivity引用,FragmentActivity和FragmentManagerImpl互相持有引用。

FragmentActivity通过FragmentController获取HostCallbacks,再通过HostCallbacks间接调用FragmentManagerImpl。FragmentManagerImpl通过HostCallbacks来间接获取上下文和执行回调方法。

添加事务操作

开启事务

获取到FragmentManagerImpl实例后,通过它的beginTransaction方法开启一个事务:
[FragmentManagerImpl.java]

public FragmentTransaction beginTransaction() {
   
    return new BackStackRecord(this);
}

这里创建BackStackRecord(继承自FragmentTransaction,并实现BackStackEntry、OpGenerator接口),BackStackRecord将持有FragmentManagerImpl。

FragmentTransaction封装一个事务,一个事务包含一组操作(单个或多个操作)。

添加add Fragment的操作

这里以,往FragmentActivity中添加一个Fragment为例。

通常添加Fragment是通过调用FragmentTransaction的add方法,传入布局ID和Fragment实例:
[FragmentTransaction.java]

public FragmentTransaction add(@IdRes int containerViewId, @NonNull Fragment fragment,
        @Nullable String tag) {
   
    // tag可选参数,可通过tag查找fragment,OP_ADD标识add操作类型
    doAddOp(containerViewId, fragment, tag, OP_ADD);
    return this;
}

BackStackRecord重写了doAddOp方法:
[BackStackRecord.java]

void doAddOp(int containerViewId, Fragment fragment, @Nullable String tag, int opcmd) {
   
    super.doAddOp(containerViewId, fragment, tag, opcmd);
    // 将持有的FragmentManagerImpl赋值给Fragment的mFragmentManager成员
    fragment.mFragmentManager = mManager;
}

该方法中通过super还是调用FragmentTransaction的doAddOp,只是在执行完后为Fragment的mFragmentManager成员赋值。

[FragmentTransaction.java]

void doAddOp(int containerViewId, Fragment fragment, @Nullable String tag, int opcmd) {
   
    final Class<?> fragmentClass = fragment.getClass();
    final int modifiers = fragmentClass.getModifiers();
    // 检查fragment是否是匿名类,或者非public访问性,或者定义在另一个类中但没有声明static
    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) {
   
        // 检查是否fragment已有tag但和当前传入的tag不同
        if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
   
            throw new IllegalStateException("Can't change tag of fragment "
                    + fragment + ": was " + fragment.mTag
                    + " now " + tag);
        }
        // fragment保存tag
        fragment.mTag = tag;
    }

    // containerViewId即为FragmentActivity中用于添加这个fragment的布局容器ID
    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");
        }
        // 检查是否fragment已经设置容器ID但与当前传入的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保存容器ID,mFragmentId默认为容器ID
        fragment.mContainerId = fragment.mFragmentId = containerViewId;
    }
    
    // 添加操作
    addOp(new Op(opcmd, fragment));
}

该方法中做了以下几步:

  1. 该方法中首先校验我们自定义的Fragment:
  • 不能是匿名类
  • 访问性必须是public
  • 若是成员类,必须是静态类
  1. 接着检查tag(若有传入)是否和fragment已有保存的不同,最后保存在fragment中
  2. 接着检查布局ID(若有传入),该布局为FragmentActivity中用于承载fragment的ViewGroup,必须有设置ID,fragment中若已有设置必须相同,最后fragment保存容器ID,mFragmentId默认也为容器ID
  3. 封装Op保存操作类型和fragment,并添加至操作集合
Op

Op表示一个操作,看它的构造函数:

[FragmentTransaction.java]

Op(int cmd, Fragment fragment) {
   
    // 保存操作类型
    this.mCmd = cmd;
    // 保存待操作的fragment
    this.mFragment = fragment;
    // 生命周期状态置为RESUMED,对应Activity执行onResume之后的状态
    this.mOldMaxState = Lifecycle.State.RESUMED;
    this.mCurrentMaxState = Lifecycle.State.RESUMED;
}
addOp

[FragmentTransaction.java]

void addOp(Op op) {
   
    // 保存Op对象
    mOps.add(op);
    // 动画相关,默认都为0
    op.mEnterAnim = mEnterAnim;
    op.mExitAnim = mExitAnim;
    op.mPopEnterAnim = mPopEnterAnim;
    op.mPopExitAnim = mPopExitAnim;
}

FragmentTransaction的mOps成员用于保存Op对象,mOps为ArrayList。

提交事务

FragmentTransaction有四个提交事务的方法:commit、commitAllowingStateLoss、commitNow、commitNowAllowingStateLoss。这四个方法都为抽象类,具体实现在BackStackRecord中。

这里以commitAllowingStateLoss为例:
[BackStackRecord.java]

public int commitAllowingStateLoss() {
   
    return commitInternal(true);
}

int commitInternal(boolean allowStateLoss) {
   
    if (mCommitted) throw new IllegalStateException("commit already called");
    // 省略DEBUG部分 ···
    mCommitted = true;
    // mAddToBackStack默认为false,若调用addToBackStack方法的话为true
    if (mAddToBackStack) {
   
        mIndex = mManager.allocBackStackIndex(this);
    } else {
   
        mIndex = -1;
    }
    // 此时传入的allowStateLoss为true
    // 通过FragmentManagerImpl入队
    mManager.enqueueAction(this, allowStateLoss);
    return mIndex;
}

接着看FragmentManagerImpl的enqueueAction方法,此时传入BackStackRecord自身和true:
[FragmentManagerImpl.java]

public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
   
    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<>();
        }
        // mPendingActions表示待执行操作集合,action即为BackStackRecord(实现了OpGenerator接口)
        mPendingActions.add(action);
        scheduleCommit();
    }
}

接着看scheduleCommit方法:
[FragmentManagerImpl.java]

void scheduleCommit() {
   
    synchronized (this) {
   
        // 标记是否存在延迟事务,默认不存在
        boolean postponeReady =
                mPostponedTransactions != null && !</
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用和提到了androidx中实现Fragment懒加载的方案。在androidx中,可以使用setMaxLifecycle()方法来设置Fragment的状态,取代了setUserVisibleHint()方法。通过在构造FragmentPagerAdapter时传入BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT参数,并将加载数据的逻辑放到Fragment的onResume()方法中,就可以实现Fragment的懒加载。这种方案可以有效地减少不必要的数据加载和提高性能。同时,androidx还引入了一些新的内容,为开发者提供了很多便利。你可以参考相关的文章和代码来深入了解和实践该方案。 引用提到了网上已经有很多关于androidxFragment懒加载的文章,但大多数只是点到了setMaxLifecycle()和修改FragmentPagerAdapter这两个方面,很少有实践的文章。因此,该文章详细记录了作者实践后的结果,可以作为更加详尽的参考。 总结起来,androidx中的Fragment懒加载可以通过setMaxLifecycle()方法和修改FragmentPagerAdapter来实现,并且androidx还引入了一些新的内容,为开发者提供了更多便利。你可以参考相关的文章和代码来了解和实践该方案。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [androidx中的Fragment懒加载方案](https://blog.csdn.net/qq_36486247/article/details/102531304)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [androidx中的Fragment懒加载](https://blog.csdn.net/tongsiw/article/details/107991830)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [androidx下的fragment的lazy懒加载问题详解](https://download.csdn.net/download/weixin_38606656/14916018)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值