当前位置: 首页 > news >正文

如何应对Android面试官 -> 玩转 Fragment

前言


image.png

本章主要讲解下 Framgent 的核心原理;

基础用法


线上基础用法,其他的可以自行百度

FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(R.id.contentlayout, "one", OneFragment);
transaction.commit();// replace
// remove
// show
// hide
// addToBackStack
// setPrimaryNavigationFragment

生命周期


image.png

Fragment 的生命周期时序图;

Fragment 的生命周期时序管理是在 FragmentActivity 中进行的,我们来看下 Fragment 的生命周期是怎么和 Activity 进行绑定的;

image.png

我们从 onCreate 方法看起,可以看到这个 FragmentActivity 有很多关于 Framgent 分发的动作;

protected void onCreate(@Nullable Bundle savedInstanceState) {// 省略部分代码this.mFragments.dispatchCreate();
}

其他的生命周期方法中 也都会有类似的动作,我们随便找一个看下:

protected void onStart() {super.onStart();this.mFragments.dispatchStart();
}

而这个 mFraments 就是一个 FragmentController,它里面定义了一系列的 dispatch 逻辑

final FragmentController mFragments = FragmentController.createController(new HostCallbacks());

image.png

也就是 FragmentActivity 持有 FragmentController 间接持有了 FragmentHostCallback,通过这样的持有方式可以操作 Fragment 相关逻辑;

而所有的 dispatch 逻辑 最终都会走到 dispatchStateChange 方法,这个方法来分发一些 Fragment 身上的状态,总共是有下面这些状态

static final int INITIALIZING = -1;          // Not yet attached.
static final int ATTACHED = 0;               // Attached to the host.
static final int CREATED = 1;                // Created.
static final int VIEW_CREATED = 2;           // View Created.
static final int AWAITING_EXIT_EFFECTS = 3;  // Downward state, awaiting exit effects
static final int ACTIVITY_CREATED = 4;       // Fully created, not started.
static final int STARTED = 5;                // Created and started, not resumed.
static final int AWAITING_ENTER_EFFECTS = 6; // Upward state, awaiting enter effects
static final int RESUMED = 7;

我们来探索下状态是怎么分发的,回到 dispatchStateChange 方法来;

private void dispatchStateChange(int nextState) {try {mExecutingActions = true;mFragmentStore.dispatchStateChange(nextState);moveToState(nextState, false);if (USE_STATE_MANAGER) {Set<SpecialEffectsController> controllers = collectAllSpecialEffectsController();for (SpecialEffectsController controller : controllers) {controller.forceCompleteAllOperations();}}} finally {mExecutingActions = false;}execPendingActions(true);
}

可以看到,不管什么状态进来,最终都是调用 moveToState 方法;

void moveToState(int newState, boolean always) {if (this.mHost == null && newState != 0) {throw new IllegalStateException("No activity");} else if (always || newState != this.mCurState) {// 赋值新传入进来的状态this.mCurState = newState;// 使用过的 Fragment 都会添加到 mAdded 中int numAdded = this.mAdded.size();Fragment f;for(int i = 0; i < numAdded; ++i) {f = (Fragment)this.mAdded.get(i);// 移动 Framgent 到期望的状态this.moveFragmentToExpectedState(f);}Iterator var6 = this.mActive.values().iterator();while(true) {do {do {if (!var6.hasNext()) {this.startPendingDeferredFragments();if (this.mNeedMenuInvalidate && this.mHost != null && this.mCurState == 4) {this.mHost.onSupportInvalidateOptionsMenu();this.mNeedMenuInvalidate = false;}return;}f = (Fragment)var6.next();} while(f == null);} while(!f.mRemoving && !f.mDetached);if (!f.mIsNewlyAdded) {this.moveFragmentToExpectedState(f);}}}
}

我们进入这个 moveFragmentToExpectedState 方法看下:

void moveFragmentToExpectedState(Fragment f) {if (f != null) {if (!this.mActive.containsKey(f.mWho)) {if (DEBUG) {Log.v("FragmentManager", "Ignoring moving " + f + " to state " + this.mCurState + "since it is not added to " + this);}} else {int nextState = this.mCurState;if (f.mRemoving) {if (f.isInBackStack()) {nextState = Math.min(nextState, 1);} else {nextState = Math.min(nextState, 0);}}this.moveToState(f, nextState, f.getNextTransition(), f.getNextTransitionStyle(), false);if (f.mView != null) {Fragment underFragment = this.findFragmentUnder(f);if (underFragment != null) {View underView = underFragment.mView;ViewGroup container = f.mContainer;int underIndex = container.indexOfChild(underView);int viewIndex = container.indexOfChild(f.mView);if (viewIndex < underIndex) {container.removeViewAt(viewIndex);container.addView(f.mView, underIndex);}}if (f.mIsNewlyAdded && f.mContainer != null) {if (f.mPostponedAlpha > 0.0F) {f.mView.setAlpha(f.mPostponedAlpha);}f.mPostponedAlpha = 0.0F;f.mIsNewlyAdded = false;AnimationOrAnimator anim = this.loadAnimation(f, f.getNextTransition(), true, f.getNextTransitionStyle());if (anim != null) {if (anim.animation != null) {f.mView.startAnimation(anim.animation);} else {anim.animator.setTarget(f.mView);anim.animator.start();}}}}if (f.mHiddenChanged) {this.completeShowHideFragment(f);}}}
}

这个方法也调度到了 moveToState 方法,我们来分析这个方法,这个方法比较长,我们一点一点来看

void moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive) {switch (f.mState) {case 0:this.dispatchOnFragmentPreAttached(f, this.mHost.getContext(), false);// 执行到这里,就会分发 Framgent 的 onAttach 方法f.performAttach();this.dispatchOnFragmentAttached(f, this.mHost.getContext(), false);// 如果没有创建,执行 createif (!f.mIsCreated) {this.dispatchOnFragmentPreCreated(f, f.mSavedFragmentState, false);f.performCreate(f.mSavedFragmentState);this.dispatchOnFragmentCreated(f, f.mSavedFragmentState, false);} else {f.restoreChildFragmentState(f.mSavedFragmentState);f.mState = 1;}}
}

performAttach 方法:

void performAttach() {for (OnPreAttachedListener listener: mOnPreAttachedListeners) {listener.onPreAttached();}mOnPreAttachedListeners.clear();mChildFragmentManager.attachController(mHost, createFragmentContainer(), this);mState = ATTACHED;mCalled = false;// 回调 Fragment 的 onAttach 方法onAttach(mHost.getContext());if (!mCalled) {throw new SuperNotCalledException("Fragment " + this+ " did not call through to super.onAttach()");}mFragmentManager.dispatchOnAttachFragment(this);mChildFragmentManager.dispatchAttach();
}

同理 performCreate 方法

void performCreate(Bundle savedInstanceState) {mChildFragmentManager.noteStateNotSaved();// 同时更新状态,是为了让下一个状态能流转起来;mState = CREATED;mCalled = false;if (Build.VERSION.SDK_INT >= 19) {mLifecycleRegistry.addObserver(new LifecycleEventObserver() {@Overridepublic void onStateChanged(@NonNull LifecycleOwner source,@NonNull Lifecycle.Event event) {if (event == Lifecycle.Event.ON_STOP) {if (mView != null) {mView.cancelPendingInputEvents();}}}});}mSavedStateRegistryController.performRestore(savedInstanceState);// 回调 Fragment 的 onCreate 方法onCreate(savedInstanceState);mIsCreated = true;if (!mCalled) {throw new SuperNotCalledException("Fragment " + this+ " did not call through to super.onCreate()");}mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
}

所有的 performXXX 方法都执行两个核心操作,一个是更新状态,是为了让下一个状态能流转起来,另一个就是回调对应的 Fragment 的 XXX 方法;

因为源码中的这个 switch 中的每一个 case 都没有 break 关键字,可以一直流转下去;

所以,无论 Fragment 是从 onAttach 到 onResume 之间的任意一个方法进来,最终都会流转到 onResume 方法;

我们接着往下看 onCreateView 逻辑

void moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive) {switch (f.mState) {case 1:f.mContainer = container;// 创建 Viewf.performCreateView(f.performGetLayoutInflater(f.mSavedFragmentState), container, f.mSavedFragmentState);// 分发 ActivityCreatedf.performActivityCreated(f.mSavedFragmentState);this.dispatchOnFragmentActivityCreated(f, f.mSavedFragmentState, false);}
}

创建 View 的过程,还是调用的 inflater 的逻辑,创建 View 之后,分发 ActivityCreated 状态

就这样一直分发下去,直到 RESUME 的状态,RESUME 之后的生命周期变化就进入了 else 的逻辑;

void moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive) { if (f.mState <= newState) {// onAttach -> onResume} else if(f.mState > newState) {// onResume -> onDestroy}
}

我们来看下 else 的逻辑,执行到这里的时候,说明 Fragment 要退后台或者销毁了;整体的流转是和上面一样,从 resume -> pause -> stop -> destroyView -> destroy 这样的一个逻辑;

所以 Fragment 的生命周期分发就是这样实现的;

FragmentManager


getSupportFragmentManager() 最终拿到的是具体实现 FragmentManagerImpl;

FragmentTransaction


FragmentTransaction 的唯一实现就是 BackStackRecord;

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

add

public FragmentTransaction add(@IdRes int containerViewId, @NonNull Fragment fragment,@Nullable String tag) {doAddOp(containerViewId, fragment, tag, OP_ADD);return this;
}

这个 OP_ADD 就是操作 Fragment 的标识

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;
void doAddOp(int containerViewId, Fragment fragment, @Nullable String tag, int opcmd) {// 省略部分代码// 创建一个 Op 对象,将 标识 和当前 Fragment 存起来了addOp(new Op(opcmd, fragment));
}

创建一个 Op 对象,将 标识 和当前 Fragment 存起来了,然后存入到一个 mOps 的集合中;

void addOp(Op op) {mOps.add(op);op.mEnterAnim = mEnterAnim;op.mExitAnim = mExitAnim;op.mPopEnterAnim = mPopEnterAnim;op.mPopExitAnim = mPopExitAnim;
}

commit

同理,其他命令也都是同样的操作;添加之后的统一处理,都在 commit 中,我们进入这个 commit 方法看下:

commit 有四个实现 commit,commitAllowingStateLoss,commitNow,commitNowAllowingStateLoss

@Override
public int commit() {return commitInternal(false);
}@Override
public int commitAllowingStateLoss() {return commitInternal(true);
}@Override
public void commitNow() {disallowAddToBackStack();mManager.execSingleAction(this, false);
}@Override
public void commitNowAllowingStateLoss() {disallowAddToBackStack();mManager.execSingleAction(this, true);
}

我们可以看到 commit 和 commitAllowingStateLoss 都是调用的 commitInternal 只是传递的值不一样,这里 true 表示可以允许状态丢失,false 不允许状态丢失,我们进入这个 commitInternal 方法看下:

int commitInternal(boolean allowStateLoss) {if (this.mCommitted) {throw new IllegalStateException("commit already called");} else {if (FragmentManager.isLoggingEnabled(2)) {Log.v("FragmentManager", "Commit: " + this);LogWriter logw = new LogWriter("FragmentManager");PrintWriter pw = new PrintWriter(logw);this.dump("  ", pw);pw.close();}this.mCommitted = true;if (this.mAddToBackStack) {this.mIndex = this.mManager.allocBackStackIndex();} else {this.mIndex = -1;}// 这里要重点关注,将事务放入一个队列中this.mManager.enqueueAction(this, allowStateLoss);return this.mIndex;}
}

enqueueAction 将当前事务放到一个队列中;

void enqueueAction(@NonNull OpGenerator action, boolean allowStateLoss) {if (!allowStateLoss) {if (this.mHost == null) {if (this.mDestroyed) {throw new IllegalStateException("FragmentManager has been destroyed");}throw new IllegalStateException("FragmentManager has not been attached to a host.");}this.checkStateLoss();}synchronized(this.mPendingActions) {if (this.mHost == null) {if (!allowStateLoss) {throw new IllegalStateException("Activity has been destroyed");}} else {// 将当前事务放到了一个集合中,用来存放事务的集合this.mPendingActions.add(action);this.scheduleCommit();}}
}

mPendingActions.add(action) 将当前事务放到了一个集合中,用来存放事务的集合,然后调用 scheduleCommit 进行提交;

void scheduleCommit() {synchronized(this.mPendingActions) {boolean postponeReady = this.mPostponedTransactions != null && !this.mPostponedTransactions.isEmpty();boolean pendingReady = this.mPendingActions.size() == 1;if (postponeReady || pendingReady) {this.mHost.getHandler().removeCallbacks(this.mExecCommit);// 通过 handler 执行this.mHost.getHandler().post(this.mExecCommit);this.updateOnBackPressedCallbackEnabled();}}
}

this.mHost.getHandler().post(this.mExecCommit) 通过 handler 执行这个命令

private Runnable mExecCommit = new Runnable() {public void run() {FragmentManager.this.execPendingActions(true);}
};

最终调度到 execPendingActions 这个方法中;也就是事务的执行是在主线程执行的;

boolean execPendingActions(boolean allowStateLoss) {this.ensureExecReady(allowStateLoss);boolean didSomething;// 重点关注的地方for(didSomething = false; this.generateOpsForPendingActions(this.mTmpRecords, this.mTmpIsPop); didSomething = true) {this.mExecutingActions = true;try {this.removeRedundantOperationsAndExecute(this.mTmpRecords, this.mTmpIsPop);} finally {this.cleanupExec();}}this.updateOnBackPressedCallbackEnabled();this.doPendingDeferredStart();this.mFragmentStore.burpActive();return didSomething;
}

generateOpsForPendingActions 生成操作为事务集合;

private boolean generateOpsForPendingActions(@NonNull ArrayList<BackStackRecord> records, @NonNull ArrayList<Boolean> isPop) {boolean didSomething = false;synchronized(this.mPendingActions) {if (this.mPendingActions.isEmpty()) {return false;} else {int numActions = this.mPendingActions.size();for(int i = 0; i < numActions; ++i) {// 取出事务集合中的每一项,然后调用 generateOps 方法;didSomething |= ((OpGenerator)this.mPendingActions.get(i)).generateOps(records, isPop);}this.mPendingActions.clear();this.mHost.getHandler().removeCallbacks(this.mExecCommit);return didSomething;}}
}

取出事务集合中的每一项,然后调用 generateOps 方法,这个方法主要是将集合中的数据挪到了另一个集合中,并附带了一个标记;

public boolean generateOps(@NonNull ArrayList<BackStackRecord> records, @NonNull ArrayList<Boolean> isRecordPop) {if (FragmentManager.isLoggingEnabled(2)) {Log.v("FragmentManager", "Run: " + this);}records.add(this);isRecordPop.add(false);if (this.mAddToBackStack) {this.mManager.addBackStackState(this);}return true;
}

这个标记就是标记哪些事务是添加到了回退栈中,true 也就是调用了 addToBackStack,false 没有;也就是有栈操作和无栈操作是分两个集合运行的;

事务分完之后,执行 removeRedundantOperationsAndExecute 删除裁剪操作之后并执行

private void removeRedundantOperationsAndExecute(@NonNull ArrayList<BackStackRecord> records, @NonNull ArrayList<Boolean> isRecordPop) {if (!records.isEmpty()) {if (records.size() != isRecordPop.size()) {throw new IllegalStateException("Internal error with the back stack records");} else {this.executePostponedTransaction(records, isRecordPop);int numRecords = records.size();int startIndex = 0;for(int recordNum = 0; recordNum < numRecords; ++recordNum) {boolean canReorder = ((BackStackRecord)records.get(recordNum)).mReorderingAllowed;if (!canReorder) {if (startIndex != recordNum) {// 执行关联操作,是一个优化逻辑,例如 add->remove->add 这样的话前面两步就是多余操作,可以移除掉this.executeOpsTogether(records, isRecordPop, startIndex, recordNum);}int reorderingEnd = recordNum + 1;if ((Boolean)isRecordPop.get(recordNum)) {while(reorderingEnd < numRecords && (Boolean)isRecordPop.get(reorderingEnd) && !((BackStackRecord)records.get(reorderingEnd)).mReorderingAllowed) {++reorderingEnd;}}this.executeOpsTogether(records, isRecordPop, recordNum, reorderingEnd);startIndex = reorderingEnd;recordNum = reorderingEnd - 1;}}if (startIndex != numRecords) {this.executeOpsTogether(records, isRecordPop, startIndex, numRecords);}}}
}

executeOpsTogether 执行关联操作,是一个优化逻辑,例如 add->remove->add 这样的话前面两步就是多余操作,可以移除掉

private void executeOpsTogether(@NonNull ArrayList<BackStackRecord> records, @NonNull ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {boolean allowReordering = ((BackStackRecord)records.get(startIndex)).mReorderingAllowed;boolean addToBackStack = false;if (this.mTmpAddedFragments == null) {this.mTmpAddedFragments = new ArrayList();} else {this.mTmpAddedFragments.clear();}this.mTmpAddedFragments.addAll(this.mFragmentStore.getFragments());Fragment oldPrimaryNav = this.getPrimaryNavigationFragment();int postponeIndex;for(postponeIndex = startIndex; postponeIndex < endIndex; ++postponeIndex) {BackStackRecord record = (BackStackRecord)records.get(postponeIndex);boolean isPop = (Boolean)isRecordPop.get(postponeIndex);// 这里执行的就是移除操作if (!isPop) {oldPrimaryNav = record.expandOps(this.mTmpAddedFragments, oldPrimaryNav);} else {oldPrimaryNav = record.trackAddedFragmentsInPop(this.mTmpAddedFragments, oldPrimaryNav);}addToBackStack = addToBackStack || record.mAddToBackStack;}this.mTmpAddedFragments.clear();if (!allowReordering) {FragmentTransition.startTransitions(this, records, isRecordPop, startIndex, endIndex, false, this.mFragmentTransitionCallback);}// 前面逻辑走完之后,执行到这里executeOps(records, isRecordPop, startIndex, endIndex);postponeIndex = endIndex;if (allowReordering) {ArraySet<Fragment> addedFragments = new ArraySet();this.addAddedFragments(addedFragments);postponeIndex = this.postponePostponableTransactions(records, isRecordPop, startIndex, endIndex, addedFragments);this.makeRemovedFragmentsInvisible(addedFragments);}if (postponeIndex != startIndex && allowReordering) {FragmentTransition.startTransitions(this, records, isRecordPop, startIndex, postponeIndex, true, this.mFragmentTransitionCallback);this.moveToState(this.mCurState, true);}for(int recordNum = startIndex; recordNum < endIndex; ++recordNum) {BackStackRecord record = (BackStackRecord)records.get(recordNum);boolean isPop = (Boolean)isRecordPop.get(recordNum);if (isPop && record.mIndex >= 0) {record.mIndex = -1;}record.runOnCommitRunnables();}if (addToBackStack) {this.reportBackStackChanged();}}

executeOps 执行 Fragment 的操作

private static void executeOps(@NonNull ArrayList<BackStackRecord> records, @NonNull ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {for(int i = startIndex; i < endIndex; ++i) {BackStackRecord record = (BackStackRecord)records.get(i);boolean isPop = (Boolean)isRecordPop.get(i);if (isPop) {// 有栈record.bumpBackStackNesting(-1);boolean moveToState = i == endIndex - 1;record.executePopOps(moveToState);} else {// 无栈record.bumpBackStackNesting(1);record.executeOps();}}}

这里也是分为两种,一种是有栈的,一种是无栈的,分开处理;我们来看下无栈的;

void executeOps() {int numOps = this.mOps.size();for(int opNum = 0; opNum < numOps; ++opNum) {FragmentTransaction.Op op = (FragmentTransaction.Op)this.mOps.get(opNum);Fragment f = op.mFragment;if (f != null) {f.setNextTransition(this.mTransition);}switch (op.mCmd) {case 1:f.setNextAnim(op.mEnterAnim);this.mManager.setExitAnimationOrder(f, false);// 执行 Framgent 的添加,添加之后就会走 Framgent 的生命周期this.mManager.addFragment(f);break;case 2:default:throw new IllegalArgumentException("Unknown cmd: " + op.mCmd);case 3:f.setNextAnim(op.mExitAnim);this.mManager.removeFragment(f);break;case 4:f.setNextAnim(op.mExitAnim);this.mManager.hideFragment(f);break;case 5:f.setNextAnim(op.mEnterAnim);this.mManager.setExitAnimationOrder(f, false);this.mManager.showFragment(f);break;case 6:f.setNextAnim(op.mExitAnim);this.mManager.detachFragment(f);break;case 7:f.setNextAnim(op.mEnterAnim);this.mManager.setExitAnimationOrder(f, false);this.mManager.attachFragment(f);break;case 8:this.mManager.setPrimaryNavigationFragment(f);break;case 9:this.mManager.setPrimaryNavigationFragment((Fragment)null);break;case 10:this.mManager.setMaxLifecycle(f, op.mCurrentMaxState);}if (!this.mReorderingAllowed && op.mCmd != 1 && f != null) {this.mManager.moveFragmentToExpectedState(f);}}if (!this.mReorderingAllowed) {this.mManager.moveToState(this.mManager.mCurState, true);}}

this.mManager.addFragment(f) 执行 Framgent 的添加,添加之后通过 moveToState 就会执行对应的生命周期

moveToState -> moveFragmentToExpectedState -> moveToState 就会到我们前面看到的Fragment生命周期的 moveToState 来分发对应的生命周期

void moveToState(@NonNull Fragment f, int newState) {FragmentStateManager fragmentStateManager = this.mFragmentStore.getFragmentStateManager(f.mWho);if (fragmentStateManager == null) {fragmentStateManager = new FragmentStateManager(this.mLifecycleCallbacksDispatcher, f);fragmentStateManager.setFragmentManagerState(1);}newState = Math.min(newState, fragmentStateManager.computeMaxState());if (f.mState <= newState) {if (f.mState < newState && !this.mExitAnimationCancellationSignals.isEmpty()) {this.cancelExitAnimation(f);}switch (f.mState) {case -1:if (newState > -1) {if (isLoggingEnabled(3)) {Log.d("FragmentManager", "moveto ATTACHED: " + f);}if (f.mTarget != null) {if (!f.mTarget.equals(this.findActiveFragment(f.mTarget.mWho))) {throw new IllegalStateException("Fragment " + f + " declared target fragment " + f.mTarget + " that does not belong to this FragmentManager!");}if (f.mTarget.mState < 1) {this.moveToState(f.mTarget, 1);}f.mTargetWho = f.mTarget.mWho;f.mTarget = null;}if (f.mTargetWho != null) {Fragment target = this.findActiveFragment(f.mTargetWho);if (target == null) {throw new IllegalStateException("Fragment " + f + " declared target fragment " + f.mTargetWho + " that does not belong to this FragmentManager!");}if (target.mState < 1) {this.moveToState(target, 1);}}fragmentStateManager.attach(this.mHost, this, this.mParent);}case 0:if (newState > 0) {fragmentStateManager.create();}case 1:if (newState > -1) {fragmentStateManager.ensureInflatedView();}if (newState > 1) {fragmentStateManager.createView(this.mContainer);fragmentStateManager.activityCreated();fragmentStateManager.restoreViewState();}case 2:if (newState > 2) {fragmentStateManager.start();}case 3:if (newState > 3) {fragmentStateManager.resume();}}} else if (f.mState > newState) {switch (f.mState) {case 4:if (newState < 4) {fragmentStateManager.pause();}case 3:if (newState < 3) {fragmentStateManager.stop();}case 2:if (newState < 2) {if (isLoggingEnabled(3)) {Log.d("FragmentManager", "movefrom ACTIVITY_CREATED: " + f);}if (f.mView != null && this.mHost.onShouldSaveFragmentState(f) && f.mSavedViewState == null) {fragmentStateManager.saveViewState();}FragmentAnim.AnimationOrAnimator anim = null;if (f.mView != null && f.mContainer != null) {f.mContainer.endViewTransition(f.mView);f.mView.clearAnimation();if (!f.isRemovingParent()) {if (this.mCurState > -1 && !this.mDestroyed && f.mView.getVisibility() == 0 && f.mPostponedAlpha >= 0.0F) {anim = FragmentAnim.loadAnimation(this.mHost.getContext(), this.mContainer, f, false);}f.mPostponedAlpha = 0.0F;ViewGroup container = f.mContainer;View view = f.mView;if (anim != null) {f.setStateAfterAnimating(newState);FragmentAnim.animateRemoveFragment(f, anim, this.mFragmentTransitionCallback);}container.removeView(view);if (container != f.mContainer) {return;}}}if (this.mExitAnimationCancellationSignals.get(f) == null) {this.destroyFragmentView(f);} else {f.setStateAfterAnimating(newState);}}case 1:if (newState < 1) {boolean beingRemoved = f.mRemoving && !f.isInBackStack();if (!beingRemoved && !this.mNonConfig.shouldDestroy(f)) {if (f.mTargetWho != null) {Fragment target = this.findActiveFragment(f.mTargetWho);if (target != null && target.getRetainInstance()) {f.mTarget = target;}}} else {this.makeInactive(fragmentStateManager);}if (this.mExitAnimationCancellationSignals.get(f) != null) {f.setStateAfterAnimating(newState);newState = 1;} else {fragmentStateManager.destroy(this.mHost, this.mNonConfig);}}case 0:if (newState < 0) {fragmentStateManager.detach(this.mNonConfig);}}}if (f.mState != newState) {if (isLoggingEnabled(3)) {Log.d("FragmentManager", "moveToState: Fragment state for " + f + " not updated inline; expected state " + newState + " found " + f.mState);}f.mState = newState;}}

addBackToStack

我们接下来看下 回退栈 addBackToStack

public FragmentTransaction addToBackStack(@Nullable String name) {if (!this.mAllowAddToBackStack) {throw new IllegalStateException("This FragmentTransaction is not allowed to be added to the back stack.");} else {this.mAddToBackStack = true;this.mName = name;return this;}
}

这里仅仅是把 mAddToBackStack 设置为 true,那么这个标志位在哪里使用的呢?就在前面我们说 commit 的时候;

if (this.mAddToBackStack) {this.mIndex = this.mManager.allocBackStackIndex();
}

allocBackStackIndex 这个方法我们来看下:

public int allocBackStackIndex(BackStackRecord bse) {synchronized(this) {int index;if (this.mAvailBackStackIndices != null && this.mAvailBackStackIndices.size() > 0) {index = (Integer)this.mAvailBackStackIndices.remove(this.mAvailBackStackIndices.size() - 1);if (DEBUG) {Log.v("FragmentManager", "Adding back stack index " + index + " with " + bse);}this.mBackStackIndices.set(index, bse);return index;} else {if (this.mBackStackIndices == null) {this.mBackStackIndices = new ArrayList();}// 如果是第一次,这里获取到的是 0index = this.mBackStackIndices.size();if (DEBUG) {Log.v("FragmentManager", "Setting back stack index " + index + " to " + bse);}// 将事务添加到这个集合中,并返回对应的 index;this.mBackStackIndices.add(bse);return index;}}
}

这里有一点比较饶,就是 this.mAvailBackStackIndices != null 这看起来好像一直不成立似的,就会导致一直都是走 else 逻辑进行添加,其实并不是,我们往下看有一个 freeBackStackSize 的逻辑;

public void freeBackStackIndex(int index) {synchronized(this) {// 先把对应索引的值设置为 nullthis.mBackStackIndices.set(index, (Object)null);if (this.mAvailBackStackIndices == null) {this.mAvailBackStackIndices = new ArrayList();}if (DEBUG) {Log.v("FragmentManager", "Freeing back stack index " + index);}// 把索引存入这个集合中this.mAvailBackStackIndices.add(index);}
}

当我们要释放某个索引的时候,就会先把对应索引的值设置为 null 然后将对应的索引添加到 mAvailBackStackIndices 这个集合中,当我们下次添加的时候,此时 this.mAvailBackStackIndices != null 就会成立,就能进入对应的逻辑中

// 获取集合中最后一项的值,也就是存入的第一个索引值
index = (Integer)this.mAvailBackStackIndices.remove(this.mAvailBackStackIndices.size() - 1);this.mBackStackIndices.set(index, bse);

获取对应的索引,并更新对应索引的值为对应的事务;回退栈逻辑就到这里了,我们接下来看下状态的保存和恢复。

onSaveInstanceState

通过 onSaveInstanceState 方法最终会调用到 FragmentController 的 saveAllState 方法

public Parcelable saveAllState() {return mHost.mFragmentManager.saveAllState();
}

我们进入这个 saveAllState 方法看下:

Parcelable saveAllState() {this.forcePostponedTransactions();this.endAnimatingAwayFragments();this.execPendingActions();this.mStateSaved = true;if (this.mActive.isEmpty()) {return null;} else {int size = this.mActive.size();ArrayList<FragmentState> active = new ArrayList(size);boolean haveFragments = false;Iterator var4 = this.mActive.values().iterator();while(true) {Fragment f;Fragment target;do {if (!var4.hasNext()) {if (!haveFragments) {if (DEBUG) {Log.v("FragmentManager", "saveAllState: no fragments!");}return null;}ArrayList<String> added = null;BackStackState[] backStack = null;size = this.mAdded.size();if (size > 0) {added = new ArrayList(size);Iterator var10 = this.mAdded.iterator();while(var10.hasNext()) {target = (Fragment)var10.next();added.add(target.mWho);if (target.mFragmentManager != this) {this.throwException(new IllegalStateException("Failure saving state: active " + target + " was removed from the FragmentManager"));}if (DEBUG) {Log.v("FragmentManager", "saveAllState: adding fragment (" + target.mWho + "): " + target);}}}if (this.mBackStack != null) {size = this.mBackStack.size();if (size > 0) {backStack = new BackStackState[size];for(int i = 0; i < size; ++i) {backStack[i] = new BackStackState((BackStackRecord)this.mBackStack.get(i));if (DEBUG) {Log.v("FragmentManager", "saveAllState: adding back stack #" + i + ": " + this.mBackStack.get(i));}}}}FragmentManagerState fms = new FragmentManagerState();fms.mActive = active;fms.mAdded = added;fms.mBackStack = backStack;if (this.mPrimaryNav != null) {fms.mPrimaryNavActiveWho = this.mPrimaryNav.mWho;}fms.mNextFragmentIndex = this.mNextFragmentIndex;return fms;}f = (Fragment)var4.next();} while(f == null);if (f.mFragmentManager != this) {this.throwException(new IllegalStateException("Failure saving state: active " + f + " was removed from the FragmentManager"));}haveFragments = true;FragmentState fs = new FragmentState(f);active.add(fs);if (f.mState > 0 && fs.mSavedFragmentState == null) {// 其他没什么值得关注的,重点是这里 saveFragmentBasicStatefs.mSavedFragmentState = this.saveFragmentBasicState(f);if (f.mTargetWho != null) {target = (Fragment)this.mActive.get(f.mTargetWho);if (target == null) {this.throwException(new IllegalStateException("Failure saving state: " + f + " has target not in fragment manager: " + f.mTargetWho));}if (fs.mSavedFragmentState == null) {fs.mSavedFragmentState = new Bundle();}this.putFragment(fs.mSavedFragmentState, "android:target_state", target);if (f.mTargetRequestCode != 0) {fs.mSavedFragmentState.putInt("android:target_req_state", f.mTargetRequestCode);}}} else {fs.mSavedFragmentState = f.mSavedFragmentState;}if (DEBUG) {Log.v("FragmentManager", "Saved state of " + f + ": " + fs.mSavedFragmentState);}}}
}

整体就是添加状态,其他没什么值得关注的,重点是这里 saveFragmentBasicState,我们的 Framgent 会解析 View 树,这里会把这个 View 树保存下来,我们进入这个方法看下:

Bundle saveFragmentBasicState(Fragment f) {Bundle result = null;if (this.mStateBundle == null) {this.mStateBundle = new Bundle();}f.performSaveInstanceState(this.mStateBundle);this.dispatchOnFragmentSaveInstanceState(f, this.mStateBundle, false);if (!this.mStateBundle.isEmpty()) {result = this.mStateBundle;this.mStateBundle = null;}if (f.mView != null) {// 保存这个 View 的状态this.saveFragmentViewState(f);}if (f.mSavedViewState != null) {if (result == null) {result = new Bundle();}result.putSparseParcelableArray("android:view_state", f.mSavedViewState);}if (!f.mUserVisibleHint) {if (result == null) {result = new Bundle();}result.putBoolean("android:user_visible_hint", f.mUserVisibleHint);}return result;
}

通过 saveFragmentViewState 来保存对应的 View 的状态;

void saveFragmentViewState(Fragment f) {if (f.mInnerView != null) {if (this.mStateArray == null) {this.mStateArray = new SparseArray();} else {this.mStateArray.clear();}f.mInnerView.saveHierarchyState(this.mStateArray);if (this.mStateArray.size() > 0) {f.mSavedViewState = this.mStateArray;this.mStateArray = null;}}
}

整体和 Activity 的状态保存很相似,最终也是存到了 Bundle 中,然后在界面重新创建的时候恢复

if (savedInstanceState != null) {Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);mFragments.restoreSaveState(p);
}

restoreSaveState 整体逻辑就是把存入的数据 再取出来,感兴趣的可以自行看下源码;

好了,Framgent 就讲到这里吧,感谢您的观看

下一章预告


MVC、MVP、MVVM

欢迎三连


来都来了,点个关注点个赞吧,你的支持是我最大的动力~

相关文章:

如何应对Android面试官 -> 玩转 Fragment

前言 本章主要讲解下 Framgent 的核心原理&#xff1b; 基础用法 线上基础用法&#xff0c;其他的可以自行百度 FragmentManager manager getSupportFragmentManager(); FragmentTransaction transaction manager.beginTransaction(); transaction.add(R.id.contentlayout,…...

sdbusplus:通过文件描述符传递数据

有的时候需要传递大量的数据,如果将数据通过dbus传递,会消耗大量的带宽。可以通过传递一个文件描述符替代传递数据: 以下的service通过文件描述符接收数据: //fd_service.cpp #include <sdbusplus/asio/connection.hpp> #include <sdbusplus/asio/object_server…...

HyperLPR3 车牌识别

Linux 之前安装了python3 apt install python3.8-venv cd /root python3 -m venv HyperLPR3 REM cd HyperLPR3 source HyperLPR3/bin/activate 参考 https://www.jb51.net/article/222885.htm python -m pip install hyperlpr3 里面有fastapi&#xff0c;opencv等 错误&#x…...

面试的内容

1.C的三大特性&#xff1a;封装&#xff0c;继承&#xff0c;多态 2.C11的特性 3.NULL与Nullptr的区别: nullptr是一个特殊的空指针常量&#xff0c;不能被隐式转换为其他类型。 NULL 在一些情况下可能会发生隐式类型转换 4.智能指针 5.stl/Qt stl stl容器包含哪些&…...

剪映网页版

https://www.capcut.cn/web 免费&#xff0c;免安装&#xff0c;跨平台&#xff0c;视频云合成&#xff0c;简直太好用了&#xff01;...

pgsql

创建分区表&#xff1a; PostgreSQL分区表_pg分区表-CSDN博客 创建list分区的函数 create or replace function create_list_fq(tb_name char, row_name char) returns int AS $$ declares char; beginraise notice CREATE TABLE if not exists %_% PARTITION OF % FOR VALU…...

Kotlin学习笔记 泛型

在 Kotlin 中&#xff0c;T 通常用作类型参数的占位符&#xff0c;它在实例化或传递参数时会被替换成具体的类型。 Kotlin 支持泛型&#xff0c;这意味着您可以编写可以与多种数据类型一起工作的代码&#xff0c;而不必为每种数据类型编写单独的代码。 ### 泛型类和函数 在 …...

开发者必看:Linux终端的10大装逼神器,让你的命令行炫酷起来!

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…...

20 VUE学习:插件

介绍 插件 (Plugins) 是一种能为 Vue 添加全局功能的工具代码。下面是如何安装一个插件的示例&#xff1a; import { createApp } from vueconst app createApp({})app.use(myPlugin, {/* 可选的选项 */ })一个插件可以是一个拥有 install() 方法的对象&#xff0c;也可以直接…...

python之any用法

写法对比 代码一&#xff1a; any(i for i in [0,1]) 代码2&#xff1a; any([i for i in [0,1]]) 优劣 结论&#xff1a;代码一写法更好 解释&#xff1a; 在 Python 中&#xff0c;any() 函数可以接受一个可迭代对象作为参数&#xff0c;并返回 True 如果可迭代对象…...

【前端学习——react坑】useState使用

问题 使用useState 时&#xff0c;例如 const [selectedId, setSelectedId] useState([false,true,false]);这样直接利用&#xff0c;无法引发使用selectedId状态的组件的变化&#xff0c;但是selectedId是修改了的 let tempselectedId;temp[toggledId]selectedId[toggledId…...

【前端每日基础】day28——async/await

async/await 是ES2017&#xff08;ES8&#xff09;引入的用于处理异步操作的语法糖&#xff0c;基于Promise实现。它使得异步代码看起来像同步代码&#xff0c;从而提高了代码的可读性和可维护性。以下是对 async/await 的详细讲解。 基本语法 async 函数 在一个函数前加上 as…...

错误记录:从把项目从Tomcat8.5.37转到Tomcat10.1.7

错误信息&#xff1a;在本地Servlet项目里没有报错&#xff0c;但是浏览器跳转该servlet时报错 型 异常报告 消息 实例化Servlet类[com.wangdao.lx.MyServlet1]异常 描述 服务器遇到一个意外的情况&#xff0c;阻止它完成请求。 例外情况 jakarta.servlet.ServletExceptio…...

AJAX基础知识

定义 Ajax 异步 JavaScript 和 XML &#xff08; async javascript and xml &#xff09;&#xff0c;使用 Ajax 技术网页应用能够快速地将数据更新呈现在用户界面上&#xff0c;而不需要重载&#xff08;刷新&#xff09;整个页面&#xff0c;这使得程序能够更快地回应用户的操…...

xcode依赖包package已经安装,但是提示No such module ‘Alamofire‘解决办法

明明已经通过xcode自带的swift包管理器安装好了依赖包&#xff0c;但是却还是提示&#xff1a;No such module&#xff0c;这个坑爹的xcode&#xff0c;我也只能说服气&#xff0c;但是无奈&#xff0c;没办法攻打苹果总部&#xff0c;只能自己想解决办法了 No such module Ala…...

基于Centos7 安装k8s一主两从

一、资源准备 mac下虚拟机环境搭建 1、使用搜狐的iso源 http://mirrors.sohu.com/centos/7.5.1804/isos/x86_64/CentOS-7-x86_64-Minimal-1804.iso 下载 iso镜像。 2、https://www.macwk.com/soft/vmware 下载 mac vm虚拟机 3、搭建一主两从集群所需虚拟机 4、新建虚拟机…...

基于java实现图片中任意封闭区域识别

需求&#xff1a; 在浏览器中给用户呈现一张图片&#xff0c;用户点击图片中的某些标志物&#xff0c;需要系统给出标志物的信息反馈&#xff0c;达到一个交互的作用。 比如下图中&#xff0c;点击某个封闭区域时候&#xff0c;需要告知用户点击的区域名称及图形形状特性等等。…...

闭包是什么?有什么特性?对页面有什么影响?

闭包是指在一个函数内部定义的函数&#xff0c;并且该函数可以访问到外部函数的变量。闭包可以将外部函数的变量保持在内存中&#xff0c;并且不会被释放。 闭包具有以下特性&#xff1a; 1. 函数内部定义的函数可以访问外部函数的变量。 2. 外部函数的变量可以保持在内存中&a…...

MS Excel: 高亮当前行列 - 保持原有格式不被改变

本文使用条件格式VBA的方法实现高亮当前行列&#xff0c;因为纯VBA似乎会清除原有的高亮格式。效果如下&#xff1a;本文图省事就使用同一种颜色了。 首先最重要的&#xff0c;【选中你期望高亮的单元格区域】&#xff0c;比如可以全选当前sheet的全部区域 然后点击【开始】-【…...

langchain学习(十三)

一、将其他chain的输入作为新chain的输出&#xff0c;三种方式 1、采用连接符"|"&#xff0c;推荐 2、采用lamba表达式输入 3、采用pipe方法 from langchain_community.chat_models import ChatOllama from langchain_core.output_parsers import StrOutputParse…...

【Nginx】深入解析Nginx配置文件

Nginx&#xff08;发音为“engine-ex”&#xff09;是一个高性能的HTTP和反向代理服务器&#xff0c;也是一个IMAP/POP3/SMTP代理服务器。由于其高并发性、低资源消耗和模块化设计&#xff0c;Nginx在全球范围内被广泛使用。本文将深入解析Nginx配置文件&#xff0c;帮助读者了…...

深入了解Nginx(一):Nginx核心原理

一、Nginx核心原理 本节为大家介绍Nginx的核心原理,包含Reactor模型、Nginx的模块化设计、Nginx的请求处理阶段. &#xff08;本文源自微博客,且已获得授权&#xff09; 1.1、Reactor模型 Nginx对高并发IO的处理使用了Reactor事件驱动模型。Reactor模型的基本组件包含时间收集…...

产品经理-流程图结构图(四)

1. 流程图 1.1 概念 为了达到特定的目标而进行的一系列有逻辑性的操作步骤&#xff0c;由两个及以上的步骤&#xff0c;完成一个完整的行为的过程&#xff0c;可称之为流程 1.2 产品经理为什么需要绘制流程图&#xff1f; 保证产品的使用逻辑合理顺畅向项目组其他成员清晰的…...

15、Spring系统-AOP

ProxyFactory选择cglib或jdk动态代理原理 ProxyFactory在生成代理对象之前需要决定到底是使用JDK动态代理还是CGLIB技术&#xff1a; 代理对象创建过程 JdkDynamicAopProxy 在构造JdkDynamicAopProxy对象时&#xff0c;会先拿到被代理对象自己所实现的接口&#xff0c;并且…...

服务器感染了. rmallox勒索病毒,如何确保数据文件完整恢复?

导言&#xff1a; 近年来&#xff0c;随着信息技术的飞速发展&#xff0c;网络安全问题日益凸显。其中&#xff0c;勒索病毒作为一种严重的网络威胁&#xff0c;对个人和企业数据造成了巨大的威胁。本文将重点介绍.rmallox勒索病毒的特点、传播途径以及应对策略&#xff0c;旨…...

[每日一练]按日期分组销售产品的最优解法

该题目来自于力扣的pandas题库&#xff0c;链接如下&#xff1a; 1484. 按日期分组销售产品 - 力扣&#xff08;LeetCode&#xff09; 题目要求&#xff1a; 表 Activities&#xff1a; ---------------------- | 列名 | 类型 | ---------------------- | sell_…...

免费wordpress中文主题

免费大图wordpress主题 首页是一张大图的免费wordpress主题模板。简洁实用&#xff0c;易上手。 https://www.jianzhanpress.com/?p5857 免费WP模板下载 顶部左侧导航条的免费WP模板&#xff0c;后台简洁&#xff0c;新手也可以下载使用。 https://www.jianzhanpress.com/…...

单链表经典算法题理解

目录 1. 前言&#xff1a; 2. 移除链表元素 3. 反转链表 4. 合并两个有序链表 5. 链表的中间节点 6. 环形链表的约瑟夫问题 7. 分割链表 1. 前言&#xff1a; 当我们学习了单链表之后&#xff0c;我能可以尝试的刷一下题了&#xff0c;以下分享一下几道题的解法 2. 移…...

STM32的时钟介绍

目录 前言1. 简介1.1 时钟是用来做什么的1.2 时钟产生的方式 2. 时钟树的组成2.1 时钟源2.1.1 内部时钟2.1.2 外部时钟 2.2 PLL锁相环2.3 SYSCLK2.4 AHB和HCLK2.5 APB和PCLK2.6 总结 3. STM32时钟的如何进行工作4.我的疑问4.1 使用MSI和HSI有什么区别吗&#xff1f;4.2 MSI的频…...

FindBI学习总结

大数据分析BI工具&#xff1a;用户只需简单拖拽便能制作出丰富多样的数据可视化信息 关注点&#xff1a; 快速入门、数据加工、构建图表和分析数据、数据分析进阶 1、界面介绍 目录–仪表板–数据准备 仪表板目录–预览区域 快速上手&#xff1a; 1、数据准备2、制作仪表板3、分…...