-
概述
在《Fragment解析(1)》中,我们分析了fragment是如何从xml中解析成fragment的,以及在这个过程中fragment的生命周期方法是如何回调的,据此,我们真正理解了一个fragment对象从创建到显示的完整逻辑,在这个基础上,本文我们从代码管理fragment的角度来看下这种方式有什么不一样的地方。
-
引出
supportFragmentManager.beginTransaction().add().replace().remove().commit()
我们知道,通过代码的方式管理fragment需要先获取一个FragmentManager,然后调用它的beginTransaction方法返回一个FragmentTransaction对象,然后调用相关的add、replace、remove等方法,最后进行commit方法完成操作,FragmentTransaction的整个操作过程是一个链式调用的过程。
-
FragmentManager
在Activity中获取FragmentManager中直接通过getSupportFragmentManager方法获取:
public FragmentManager getSupportFragmentManager() { return mFragments.getSupportFragmentManager(); }
通过前文的分析,我们知道mFragments是FragmentController,它的FragmentManager来自于mHost,mHost是HostCallback,它的mFragmentManager就是其父类的mFragmentManager,也就是FragmentManagerImpl。
对于Fragment中获取FragmentManager来说,如果要获取它上层Activity的manager则调用getParentFragmentManager方法获取,获取的是内部的mFragmentManager;如果其内部包含着其他子Fragment,要获取管理子Fragment的manager,则调用getChildFragmentManager方法,获取到的是mChildFragmentManager,它是在Fragment内部初始化的FragmentManagerImpl:
FragmentManager mFragmentManager; FragmentManager mChildFragmentManager = new FragmentManagerImpl(); public final FragmentManager getParentFragmentManager() { FragmentManager fragmentManager = mFragmentManager; if (fragmentManager == null) { throw new IllegalStateException( "Fragment " + this + " not associated with a fragment manager."); } return fragmentManager; } final public FragmentManager getChildFragmentManager() { if (mHost == null) { throw new IllegalStateException("Fragment " + this + " has not been attached yet."); } return mChildFragmentManager; }
-
beginTransaction()
要使用FragmentManager来操作fragment需要一个FragmentTransaction对象,这通过beginTransaction方法获取:
public FragmentTransaction beginTransaction() { return new BackStackRecord(this); }
可见后续的操作都是由BackStackRecord来完成的。我们以add方法为例看进去,它定义于FragmentTransaction中,如果是传递Class而不是fragment的实例的话,则会先调用createFragment方法创建:
private Fragment createFragment(@NonNull Class<? extends Fragment> fragmentClass, @Nullable Bundle args) { if (mFragmentFactory == null) { throw new IllegalStateException("Creating a Fragment requires that this " + "FragmentTransaction was built with FragmentManager.beginTransaction()"); } if (mClassLoader == null) { throw new IllegalStateException("The FragmentManager must be attached to its" + "host to create a Fragment"); } Fragment fragment = mFragmentFactory.instantiate(mClassLoader, fragmentClass.getName()); if (args != null) { fragment.setArguments(args); } return fragment; }
mFragmentFactory和mClassLoader的初始化则就要到BackStackRecord的构造中去找了:
BackStackRecord(@NonNull FragmentManager manager) { super(manager.getFragmentFactory(), manager.getHost() != null ? manager.getHost().getContext().getClassLoader() : null); mManager = manager; }
FragmentManager默认使用mHostFragmentFactory:
private FragmentFactory mHostFragmentFactory = new FragmentFactory() { @SuppressWarnings("deprecation") @NonNull @Override public Fragment instantiate(@NonNull ClassLoader classLoader, @NonNull String className) { return getHost().instantiate(getHost().getContext(), className, null); } };
instantiate在HostCallbacks的顶级父类FragmentContainer中定义:
public Fragment instantiate(@NonNull Context context, @NonNull String className, @Nullable Bundle arguments) { return Fragment.instantiate(context, className, arguments); }
public static Fragment instantiate(@NonNull Context context, @NonNull String fname, @Nullable Bundle args) { try { Class<? extends Fragment> clazz = FragmentFactory.loadFragmentClass( context.getClassLoader(), fname); Fragment f = clazz.getConstructor().newInstance(); if (args != null) { args.setClassLoader(f.getClass().getClassLoader()); f.setArguments(args); } return f; } ... }
FragmentFactory里会对fragment的class进行缓存,知道就行。
然后会调用重载add方法,最终会调用doAddOp方法:
void doAddOp(int containerViewId, Fragment fragment, @Nullable String tag, int opcmd) { ... //对fragment进行一系列标准化验证 ... addOp(new Op(opcmd, fragment)); }
void addOp(Op op) { mOps.add(op); op.mEnterAnim = mEnterAnim; op.mExitAnim = mExitAnim; op.mPopEnterAnim = mPopEnterAnim; op.mPopExitAnim = mPopExitAnim; }
可见,整个BackStackRecord的add操作就是实例化了fragment然后封装进一个Op对象,然后放入一个List中。
其他的像replace、remove等操作也是一样,都是调用的doAddOp方法,只不过每种操作的Op,它们的cmd参数不同:
static final int OP_NULL = 0; static final int OP_ADD = 1; static final int OP_REPLACE = 2; static final int OP_REMOVE = 3; static final int OP_HIDE = 4; static final int OP_SHOW = 5; static final int OP_DETACH = 6; static final int OP_ATTACH = 7; static final int OP_SET_PRIMARY_NAV = 8; static final int OP_UNSET_PRIMARY_NAV = 9; static final int OP_SET_MAX_LIFECYCLE = 10;
好了我们现在知道了BackStackRecord的add、replace等操作做了什么,但是我们没有看到和fragment生命周期相关的逻辑,这些都在最后的commit方法逻辑里。
-
commit操作
我们知道,代码fragment的操作都需要执行commit才能生效,那它是怎么把mOps中的Op利用起来的呢?
commit的实现由BackStackRecord完成,实则调用了commitInternal方法:
int commitInternal(boolean allowStateLoss) { if (mCommitted) throw new IllegalStateException("commit already called"); ... mCommitted = true; //如果放入回退栈中,则使用回退栈中的index if (mAddToBackStack) { mIndex = mManager.allocBackStackIndex(); } else { mIndex = -1; } mManager.enqueueAction(this, allowStateLoss); return mIndex; }
allowStateLoss参数用在enqueueAction方法中,这里就执行到了FragmentManager中:
void enqueueAction(@NonNull OpGenerator action, boolean allowStateLoss) { if (!allowStateLoss) { if (mHost == null) { if (mDestroyed) { throw new IllegalStateException("FragmentManager has been destroyed"); } else { throw new IllegalStateException("FragmentManager has not been attached to a " + "host."); } } checkStateLoss(); } synchronized (mPendingActions) { if (mHost == null) { if (allowStateLoss) { // This FragmentManager isn't attached, so drop the entire transaction. return; } throw new IllegalStateException("Activity has been destroyed"); } mPendingActions.add(action); scheduleCommit(); } }
可以看到,allowStateLoss为true的话则不会关心mHost是否还在,默认都是false。而且enqueueAction操作还是线程安全的。action是前面的BackStackRecord,它会被添加到mPendingActions中,然后执行scheduleCommit方法:
void scheduleCommit() { synchronized (mPendingActions) { boolean pendingReady = mPendingActions.size() == 1; if (pendingReady) { mHost.getHandler().removeCallbacks(mExecCommit); mHost.getHandler().post(mExecCommit); updateOnBackPressedCallbackEnabled(); } } }
这里可以看到,执行时要求吗PendingActions内只能有一个action,然后会执行mExeCommit:
private Runnable mExecCommit = new Runnable() { @Override public void run() { execPendingActions(true); } };
boolean execPendingActions(boolean allowStateLoss) { ... boolean didSomething = false; while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) { mExecutingActions = true; try { removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop); } finally { cleanupExec(); } didSomething = true; } ... return didSomething; }
generateOpsForPendingActions方法会把mPendingActions中的信息填充到mTmpRecords和mTmpIsPop中(如果是单线程操作的话mPendingActions只含一个),然后mPendingActions对于当前这个操作就没有用了:
private boolean generateOpsForPendingActions(@NonNull ArrayList<BackStackRecord> records, @NonNull ArrayList<Boolean> isPop) { boolean didSomething = false; synchronized (mPendingActions) { if (mPendingActions.isEmpty()) { return false; } try { final int numActions = mPendingActions.size(); for (int i = 0; i < numActions; i++) { didSomething |= mPendingActions.get(i).generateOps(records, isPop); } } finally { // Whether generateOps succeeds or not, we clear the pending actions // to avoid re-processing the same set of actions a second time mPendingActions.clear(); mHost.getHandler().removeCallbacks(mExecCommit); } } return didSomething; }
这里把mPendingActions及时清空说明操作只会执行一次,而且这样做会为其他线程的操作腾出mPengdingActions的使用(因为mPengdingActions里同时只能存在一个action),可见,mPendingActions就是用来临时存放和安排要执行的action。
因为mPengdingActions中的数据是BackStackRecord,因此generateOps方法在BackStackRecord中实现:
public boolean generateOps(@NonNull ArrayList<BackStackRecord> records, @NonNull ArrayList<Boolean> isRecordPop) { ... records.add(this); isRecordPop.add(false); //在这里将记录放入回退栈中 if (mAddToBackStack) { mManager.addBackStackState(this); } return true; }
回到execPendingActions方法中,接下来会对每一对mTmpRecords和mTmpIsPop调用removeRedundantOperationsAndExecute方法:
private void removeRedundantOperationsAndExecute(@NonNull ArrayList<BackStackRecord> records, @NonNull ArrayList<Boolean> isRecordPop) { ... final int numRecords = records.size(); int startIndex = 0; ... executeOpsTogether(records, isRecordPop, startIndex, numRecords); ... }
这里其实有一个排序的逻辑,和我们要分析的东西没啥关系,这里不去关心。只要知道调用了executeOpsTogether方法即可:
private void executeOpsTogether(@NonNull ArrayList<BackStackRecord> records, @NonNull ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) { ... boolean addToBackStack = false; if (mTmpAddedFragments == null) { mTmpAddedFragments = new ArrayList<>(); } else { mTmpAddedFragments.clear(); } mTmpAddedFragments.addAll(mFragmentStore.getFragments()); Fragment oldPrimaryNav = getPrimaryNavigationFragment(); for (int recordNum = startIndex; recordNum < endIndex; recordNum++) { final BackStackRecord record = records.get(recordNum); final boolean isPop = isRecordPop.get(recordNum); if (!isPop) { oldPrimaryNav = record.expandOps(mTmpAddedFragments, oldPrimaryNav); }... addToBackStack = addToBackStack || record.mAddToBackStack; } mTmpAddedFragments.clear(); ... executeOps(records, isRecordPop, startIndex, endIndex); ... // And only then do we move all other fragments to the current state moveToState(mCurState, true); ... }
expandOps方法会根据mOps里面每个Op的cmd进行操作合并,比如replace操作最终也会转成add操作,在expandOps里会找到之前的把它删除然后把新的加进去。
然后调用executePopOps方法,该方法里会根据mOps中Op的cmd调用FragmentManager的addFragment、removeFragment等方法,最终修改的就是FragmetnStore中的mActive和mAdded集合。
最后调用moveToState方法修改所有fragment的状态,moveToState我们就不赘述了,在《Fragment解析(1)》中分析过了,在那里会执行Fragement的生命周期方法。
-
关于show和hide
show和hide操作实际上会修改Fragment的mHidden和mHiddenChanged属性,在FragmetnStateManager的moveToExpectedState方法的最后会修改可见性:
if (mFragment.mHiddenChanged) { if (mFragment.mView != null && mFragment.mContainer != null) { // Get the controller and enqueue the show/hide SpecialEffectsController controller = SpecialEffectsController .getOrCreateController(mFragment.mContainer, mFragment.getParentFragmentManager()); if (mFragment.mHidden) { controller.enqueueHide(this); } else { controller.enqueueShow(this); } } if (mFragment.mFragmentManager != null) { mFragment.mFragmentManager.invalidateMenuForFragment(mFragment); } mFragment.mHiddenChanged = false; mFragment.onHiddenChanged(mFragment.mHidden); mFragment.mChildFragmentManager.dispatchOnHiddenChanged(); }
最终会在SpecialEffectsController的enqueueXxx方法中操作:
void enqueueShow(@NonNull FragmentStateManager fragmentStateManager) { enqueue(Operation.State.VISIBLE, Operation.LifecycleImpact.NONE, fragmentStateManager); } void enqueueHide(@NonNull FragmentStateManager fragmentStateManager) { enqueue(Operation.State.GONE, Operation.LifecycleImpact.NONE, fragmentStateManager); }
enqueue会接收一个finalState参数,比如上面的Operation.State.VISIBLE和Operation.State.GONE:
private void enqueue(@NonNull Operation.State finalState, @NonNull Operation.LifecycleImpact lifecycleImpact, @NonNull final FragmentStateManager fragmentStateManager) { synchronized (mPendingOperations) { ... final FragmentStateManagerOperation operation = new FragmentStateManagerOperation( finalState, lifecycleImpact, fragmentStateManager, signal); mPendingOperations.add(operation); operation.addCompletionListener(new Runnable() { @Override public void run() { if (mPendingOperations.contains(operation)) { operation.getFinalState().applyState(operation.getFragment().mView); } } }); operation.addCompletionListener(new Runnable() { @Override public void run() { mPendingOperations.remove(operation); mRunningOperations.remove(operation); } }); } }
那mPendingOperations是在什么时候被处理的呢?其实是通过SpecialEffectsController的executePendingOperations方法处理的,这个方法在很多地方都调用过,比如在mFragments.dispatchXxx系列方法之后调用,或者在executeOpsTogether方法中的moveToState方法之后调用:
moveToState(mCurState, true); Set<SpecialEffectsController> changedControllers = collectChangedControllers( records, startIndex, endIndex); for (SpecialEffectsController controller : changedControllers) { controller.updateOperationDirection(isPop); controller.markPostponedState(); controller.executePendingOperations(); }
applyState里面修改可见性:
void applyState(@NonNull View view) { switch (this) { case REMOVED: ViewGroup parent = (ViewGroup) view.getParent(); if (parent != null) { parent.removeView(view); } break; case VISIBLE: view.setVisibility(View.VISIBLE); break; case GONE: view.setVisibility(View.GONE); break; case INVISIBLE: view.setVisibility(View.INVISIBLE); break; } }
-
总结
综上可见,不管是xml加载还是通过FragmentManager直接管理,最终操作的都是FragmentStore中mAdded和mActive集合,殊途同归,最终都是通过FragmentStateManager的moveToExpectedState方法来执行生命周期方法调用的,show和hide是通过控制Fragment添加到container中的mView的Visibility来实现显示/隐藏的。
网友评论