关于fragment backState的原理
在使用Fragment的時候我們一般會這樣寫:
? ? ? ? FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
? ? ? ? transaction.replace(R.id.content_view, fragment, fragment.getClass().getName());
? ? ? ? // transaction.addToBackStack(null);
? ? ? ? transaction.commitAllowingStateLoss();
對于是否要加transaction.addToBackStack(null);也就是將Fragment加入到回退棧。官方的說法是取決于你是否要在回退的時候顯示上一個Fragment。
雖然知道這回事,但是在做項目的時候還是沒有清楚的認識,只是習慣性的加上addToBackStack;查看源碼后才了解到該神馬時候加入回退棧。
首先看
? ?void addBackStackState(BackStackRecord state) {
? ? ? ? if (mBackStack == null) {
? ? ? ? ? ? mBackStack = new ArrayList<BackStackRecord>();
? ? ? ? }
? ? ? ? mBackStack.add(state);
? ? ? ? reportBackStackChanged();
? ? }
可以看出,我們并不是將Fragment加入到回退棧,而是加了一個叫BackStackRecord的實例;那這個BackStackRecord到底是什么,簡單的說一個BackStackRecord記錄了一次操作。
final class BackStackRecord extends FragmentTransaction implements?FragmentManager.BackStackEntry, Runnable
backstackRecord繼承了FragmentTransaction抽象類,獲得了諸如add,remove,replace這些控制方法,所以我們控制Fragment時一直使用的getSupportFragmentManager().beginTransaction()其實就是返回一個BackStackRecord實例;
backstackRecord也維護了一個Op對象,Op對象的作用就是記錄一次操作的動作和Fragment引用以及操作使用的動畫;
? ?static final class Op {
? ? ? ? Op next;
? ? ? ? Op prev;
? ? ? ? int cmd;
? ? ? ? Fragment fragment;
? ? ? ? int enterAnim;
? ? ? ? int exitAnim;
? ? ? ? int popEnterAnim;
? ? ? ? int popExitAnim;
? ? ? ? ArrayList<Fragment> removed;
? ? }
最后backstackRecord也實現了Runnable接口,通過commit來啟動自身,在run方法中又根據維護的Op對象進行不同的操作。其實不同的Fragment操作就是在啟動不同的BackStatcRecord線程。
下面我們已一次transaction.add操作為例:此操作也就是調用BackStackRecord里的add方法,方法中維護一個Op來保存這次add操作和相應的Fragment;然后我們會調用commit方法來提交操作,實質上是啟動實現了Runnable接口的BackStackRecord自身,在run方法中根據Op執行add分支的操作,這里面我們會調用FragmentManager的addFragment方法
? ?public void run() {
? ? ? ? ? ? ......
? ? ? ? ? ? switch (op.cmd) {
? ? ? ? ? ? ? ? case OP_ADD: {
? ? ? ? ? ? ? ? ? ? Fragment f = op.fragment;
? ? ? ? ? ? ? ? ? ? f.mNextAnim = op.enterAnim;
? ? ? ? ? ? ? ? ? ? mManager.addFragment(f, false);
? ? ? ? ? ? ? ? } break;
? ? ? ? ? ? ......
? ? ? ??mManager.moveToState(mManager.mCurState, mTransition,?mTransitionStyle, true);
? ? ? ? if (mAddToBackStack) {? ? ? ? ? ? mManager.addBackStackState(this);
? ? ? ? }
? ? }
? ? 注意方法的最后會根據mAddToBackStack標識來判斷是否加入到回退棧。
接下來在FragmentManager的addFragment方法中
? ? public void addFragment(Fragment fragment, boolean moveToStateNow) {
? ? ? ? if (mAdded == null) {
? ? ? ? ? ? mAdded = new ArrayList<Fragment>();
? ? ? ? }
? ? ? ? if (DEBUG) Log.v(TAG, "add: " + fragment);
? ? ? ? makeActive(fragment); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //通過此方法將fragment加入到一個mActive列表里。
? ? ? ? if (!fragment.mDetached) {
? ? ? ? ? ? if (mAdded.contains(fragment)) {
? ? ? ? ? ? ? ? throw new IllegalStateException("Fragment already added: " + fragment);
? ? ? ? ? ? }
? ? ? ? ? ? mAdded.add(fragment);
? ? ? ? ? ? fragment.mAdded = true;
? ? ? ? ? ? fragment.mRemoving = false;
? ? ? ? ? ? if (fragment.mHasMenu && fragment.mMenuVisible) {
? ? ? ? ? ? ? ? mNeedMenuInvalidate = true;
? ? ? ? ? ? }
? ? ? ? ? ? if (moveToStateNow) {
? ? ? ? ? ? ? ??moveToState(fragment);
? ? ? ? ? ? }
? ? ? ? }
? ? }
? 像上面注釋里說的,通過makeActive方法將fragment加入到一個mActive列表。這個列表在后面會用到。但現在先來看看代碼里用藍色標記的兩個方法,這是兩個方法名相同的重載方法,他們最后都會調用一個非常重要的方法:moveToState
? ? void moveToState(Fragment f, int newState, int transit, int transitionStyle,?boolean keepActive) {
? ? ? ?......
? ? ? ? if (f.mState < newState) {
? ? ? ? ? ?......
? ? ? ? ? ? switch (f.mState) {
? ? ? ? ? ? ? ? case Fragment.INITIALIZING:
? ? ? ? ? ? ? ? ? ? ......
? ? ? ? ? ? ? ? case Fragment.CREATED:
? ? ? ? ? ? ? ? ? ? if (newState > Fragment.CREATED) {
? ? ? ? ? ? ? ? ? ? ......
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? case Fragment.ACTIVITY_CREATED:
? ? ? ? ? ? ? ? case Fragment.STOPPED:
? ? ? ? ? ? ? ? ? ? if (newState > Fragment.STOPPED) {
? ? ? ? ? ? ? ? ? ? ? ??f.performStart();
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? case Fragment.STARTED:
? ? ? ? ? ? ? ? ? ? if (newState > Fragment.STARTED) {
? ? ? ? ? ? ? ? ? ? ? ?......
? ? ? ? ? ? ? ? ? ? ? ??f.performResume();
? ? ? ? ? ? ? ? ? ? ? ?......
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? } else if (f.mState > newState) {
? ? ? ? ? ? switch (f.mState) {
? ? ? ? ? ? ? ? case Fragment.RESUMED:
? ? ? ? ? ? ? ? ? ? if (newState < Fragment.RESUMED) {
? ? ? ? ? ? ? ? ? ? ? ?......
? ? ? ? ? ? ? ? ? ? ? ??f.performPause();
? ? ? ? ? ? ? ? ? ? ? ? ......
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? case Fragment.STARTED:
? ? ? ? ? ? ? ? ? ? if (newState < Fragment.STARTED) {
? ? ? ? ? ? ? ? ? ? ??? f.performStop();
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? case Fragment.STOPPED:
? ? ? ? ? ? ? ? ? ? if (newState < Fragment.STOPPED) {
? ? ? ? ? ? ? ? ? ? ? ??f.performReallyStop();
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? case Fragment.ACTIVITY_CREATED:
? ? ? ? ? ? ? ? ? ? if (newState < Fragment.ACTIVITY_CREATED) {
? ? ? ? ? ? ? ? ? ? ? ?......
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? case Fragment.CREATED:
? ? ? ? ? ? ? ? ? ? if (newState < Fragment.CREATED) {
? ? ? ? ? ? ? ? ? ? ? ? ......
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??f.performDestroy();
? ? ? ? ? ? ? ? ? ? ? ? ......
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ??
? ? ? ? f.mState = newState;
? ? }?? ? ? ? ? ? ??
對于這個方法要說明三點:
第一:方法里所有的分支只有
? ? static final int INITIALIZING = 0; ? ? // Not yet created.
? ? static final int CREATED = 1; ? ? ? ? ?// Created.
? ? static final int ACTIVITY_CREATED = 2; // The activity has finished its creation.
? ? static final int STOPPED = 3; ? ? ? ? ?// Fully created, not started.
? ? static final int STARTED = 4; ? ? ? ? ?// Created and started, not resumed.
? ? static final int RESUMED = 5; ? ? ? ? ?// Created started and resumed.
這六種狀態,好像不太夠。其實這很好理解,如果傳來的新狀態比fragment的當前狀態大那就是處于創建過程,如果新狀態比當前狀態小那就是處于關閉過程。閉上眼睛想一想就能轉過彎兒了!!!
第二:這里面所有的case分支都是沒有break方法的,這樣就能保證傳來一個狀態就能把這個狀態之后的所有操作都執行一遍,例如創建時傳INITIALIZING狀態,就能執行INITIALIZING、CREATED、ACTIVITY_CREATED、STOPPED、STARTED這一流程的代碼,而不需要我們挨個的每個狀態都傳;又例如我們重寫回到fragment要調用start()方法,那只需要傳STOPPED(創建時執行的是onStart)就可以,而不需要再傳STARTED(創建時執行的是onResume)。
第三:代碼中的紅色部分會調用FragmentActivity里的dispatchActivityXXX 方法,這里面最終會調用另外一個重要方法,它也叫做moveToState(其實這個方法最終也是會去調用上面的moveToState方法):
? ?void moveToState(int newState, int transit, int transitStyle, boolean always) {
? ? ? ? ......
? ? ? ? ? ? for (int i=0; i<mActive.size(); i++) {
? ? ? ? ? ? ? ? Fragment f = mActive.get(i);
? ? ? ? ? ? ? ? if (f != null) {
? ? ? ? ? ? ? ? ? ? moveToState(f, newState, transit, transitStyle, false);
? ? ? ? ? ? ? ? ? ? if (f.mLoaderManager != null) {
? ? ? ? ? ? ? ? ? ? ? ? loadersRunning |= f.mLoaderManager.hasRunningLoaders();
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? if (!loadersRunning) {
? ? ? ? ? ? ? ? startPendingDeferredFragments();
? ? ? ? ? ? }
? ? ? ? }
? ? }
這里面有個for循環,它會根據前面提到的mActive列表來調用存儲fragment的moveToState方法(是上面的那個moveToState)。所以如果我們使用show、hide而不是用add、remove來操作fragment顯示與隱藏的話,就會發現一個問題,假設一個FragmentActivity已經創建了三個fragment并且隱藏,然后它在創建第四個fragment的時候,會發現已經隱藏的三個fragment也都運行了onresume方法。這就是因為這三個fragment已經加入到mActive中,并且在創建第四個的時候循環調用了他們的resume方法。
現在回到最開始的問題,為什么說加入回退棧就可以實現按返回鍵退回到上一個fragment界面:
這就要看FragmentActivity里面的回退方法了
public void onBackPressed() {
? ? ? ? if (!mFragments.popBackStackImmediate()) {
? ? ? ? ? ? finish();
? ? ? ? }
? ? }
關鍵在判斷條件,也就是popBackStackImmediate()方法的實現和他的返回值:
他的返回值是由popBackStackState(mActivity.mHandler, null, -1, 0)提供的(注意參數是固定的)
? ? boolean popBackStackState(Handler handler, String name, int id, int flags) {
? ? ? ? if (mBackStack == null) {
? ? ? ? ? ? return false;
? ? ? ? }
? ? ? ? if (name == null && id < 0 && (flags&POP_BACK_STACK_INCLUSIVE) == 0) {
? ? ? ? ? ??int last = mBackStack.size()-1;
? ? ? ? ? ? if (last < 0) {
? ? ? ? ? ? ? ? return false;
? ? ? ? ? ? }
? ? ? ? ? ? final BackStackRecord bss = mBackStack.remove(last);
? ? ? ? ? ? bss.popFromBackStack(true);
? ? ? ? ? ? reportBackStackChanged();
? ? ? ? } else {
? ? ? ? ? ......
? ? ? ? }
? ? ? ? return true;
? ? }
注意方法的第一個判斷條件:如果mBackStack ?== null 就直接return false,這樣就會直接執行FragmentActivity的finishi()方法,這也就是當我們不添加addToBackStack方法時按返回鍵不會返回上一個fragment界面而是直接退出程序的原因了。
若添加了addToBackStack方法,也就是mBackStack != null 的情況下,根據固定的參數會進入藍色代碼段,在這里取出回退棧列表中的最后一條BackStackReco記錄并執行它的popFromBackStack方法:
在這個方法里會根據BackStackRecord維護的Op對象來執行相應的操作,以replace操作為例:
? ? case OP_REPLACE: {
? ? ? ? ? ? ? ? ? ? Fragment f = op.fragment;
? ? ? ? ? ? ? ? ? ? if (f != null) {
? ? ? ? ? ? ? ? ? ? ? ? f.mNextAnim = op.popExitAnim;
? ? ? ? ? ? ? ? ? ? ? ? mManager.removeFragment(f,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? FragmentManagerImpl.reverseTransit(mTransition),
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? mTransitionStyle);
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? if (op.removed != null) {
? ? ? ? ? ? ? ? ? ? ? ? for (int i=0; i<op.removed.size(); i++) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? Fragment old = op.removed.get(i);
? ? ? ? ? ? ? ? ? ? ? ? ? ? old.mNextAnim = op.popEnterAnim;
? ? ? ? ? ? ? ? ? ? ? ? ? ? mManager.addFragment(old, false);
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? } break;
從中可以清除的看出是把Op的當前fragment給remove掉,再把Op保存的old fragment給add上,這樣一來就會顯示上一個界面了。
所以,根據不同的情景,當我們不需要fragment會退到上一個界面或者管理的fragment過多而不想保留BackStackRecord記錄過度使用資源時,就可以加入回退棧。
總結
以上是生活随笔為你收集整理的关于fragment backState的原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关于fragment backstate
- 下一篇: 关于Fragment、Tabhost和F