关于Fragment的一些分析

生命周期:
onAttach(Context):Fragment和Activity建立链接。

onCreate(Bundle):进行部分初始化,当该函数执行时,Activity仍走在生命周期中,并未走到onResume。你不可以依赖类似于Activity的view的东西。Bundle可用于异常恢复。

onCreateView(LayoutInflater, ViewGroup, Bundle):当你在此return了view后,要在onDestroyView中释放。(当Fragment用于处理逻辑而没有添加view时,可以返回null)

onActivityCreated(Bundle):Activity和Fragment都已经构建完毕。可以retrieving view或者restoring state。
在此时也可以调用 [setRetainInstance(boolean)当Activity被重建时(例如Configuation改变了),可以调用这个函数保持Fragment的实例引用。此函数只可以在Fragment不在回退栈时使用。如果调用了该函数,当Activity recreated时,Fragment的生命周期会完全不同。onDestroy不会被调用(onDetach依然会调用),onCreate不会再次调用(因为Fragment不会recreated),onAttach和onActivityCreated依然会被调用

onViewStateRestored():用于Fragment恢复曾保存的view。此函数会在onActivityCreated(Bundle)之后before onStart()之前调用。

onStart():Fragment可见, 通常和Activity的onStart密切联系。

onResume():Fragment可见,Activity正在运行。

onPause():用户无法与Fragment建立联系。Activity正处于onPause或Fragment正被activity操作.这通常和Activity的onPause密切联系

onStop():对用户不可见。.这通常和Activity的onStop密切联系

onDestroyView():Fragment清理和View连接的所有资源,无论onCreateView是否返回null,此函数都会被调用。在View的状态保存之后,在它从parent移除前调用。

onDestroy():Fragment不再使用,此处应做最后的内存清理工作。

onDetach():Fagment不再和Activity建立连接


首先理一下FragmentActivity, FragmentManager, FragmentTransition之间的关系。

这里写图片描述

看图可能很难完整理解,这里讲解一下。HostCallback是FragmentActivity的内部类,FragmentActivity持有FramentController的实例,它将HostCallback的实例传给FragmentController。而FragmentHostCallback持有FragmentMangertImpl的实例。也就是说getSupportFragmentManager是通过控制HostCallback返回FragmentManagerImpl。
而beginTransaction实际上是返回BackStackRecord的实例。
总的来说,一个FragmentActivity持有一个FragmentManager的实例,而FragmentManager持有多个FragmentTransaction的实例。

FragmentContainer:回调Fragment的容器,里面只有两个方法。

  • onFindViewById(返回Id代表的View,也就是Fragment的onCreateView中的ViewGroup)。
  • onHasView(判断容器是否含有view)。

FragmentHostCallback: 集成对Fragment的控制。里面持有Activity、Context、HandlerFragmentManagerImpl,LoaderManagerImpl等引用。里面的方法主要有(只列出部分):

  • onShouldSaveFragmentState(是否应该保存Fragment的状态)。
  • onGetHost(返回正控制着Fragment的对象,如果Fragment被FragmentActivity控制, 此方法返回的对象与getActivity一样)。
  • onStartActivityFromFrgment(这个方法实际上只是调用了context的startActivity)。
  • getActivity。
  • getContext。
  • getHandler。

HostCallback:继承了FragmentHostCallback,重写了startActivityFromFragment等方法。

FragmentManager:控制Fragment和Activity之间的交互。里面的方法主要有(只列出部分):

  • beginTransaction:不可以在onSaveInstanceState后使用。最好别在onStart,onResume中使用,容易导致状态丢失(后面再讨论)。
  • executePendingTransaction:在commit之后调用,在主线程中调用异步线程,该方法只可以从主线程中执行,Fraagmen中所的有回调方法,对fragment的操作都通过该方法完成。
  • findFragmentById/findFragmentByTag:Finds a fragment that was identified by the given id either when inflated from XML or as the container ID when added in a transaction(这段我翻译的比较烂).查找时,先从已经添加进Activity中的Fragment中查找,再在回退栈中查找。
  • popBackStack():弹出栈中最顶端的BackStackRecord,此函数是异步的,它将请求排入队列,但不会立刻执行,直到主线程有空时才执行。
  • popBackStackImmediate:类似executePendingTransaction,但会立刻执行。
  • PopBackStack(String name, int flags):name不为空时,弹出符合该name的BackStateRecord之前的所有的BackStateRecord。而flags只有两个选择:0和POP_BACK_STACK_INCLUSIVE。这个参数用来控制是否把自身弹出,如果为空,则把自身弹出。设置了POP_BACK_STACK_INCLUSIVE后,满足Tag/id的BackStateRecord会被弹出,知道不匹配或已经到了栈底。
  • getBackStackEntryCount:返回当前Stack中的BackStateRecord数量
  • putFragment:将Fragment的索引存入Bundle,通过getFragment可以恢复相同Fragment。
  • getFragment:恢复Fragment的实例
  • saveFragmentInstanceState:保存Fragment的实例状态,此函数在建立了一个Fragment的实例和添加进FragmentManager后使用。该函数有几个注意事项:调用该函数的Fragment必须在FragmentManager中,不可以调用putFragment,也不可以依赖于其他Fragment。

FragmentManagerImpl:继承了FragmentManager。看以下代码:

    ArrayList<Runnable> mPendingActions;
    Runnable[] mTmpActions;//正在执行的任务
    boolean mExecutingActions;//待执行的任务,调用add,replace等之后会把任务压进这里等待执行。

    ArrayList<Fragment> mActive;//
    ArrayList<Fragment> mAdded;//
    ArrayList<BackStackRecord> mBackStack;//回退栈

    ArrayList<OnBackStackChangedListener> mBackStackChangeListeners;//回退栈发生变化时回调

    int mCurState = Fragment.INITIALIZING;//代表Fragment应该到达的生命周期,这个变量会和Fragment的mState进行比较,进而决定Fragment应该到达哪个生命周期
    FragmentHostCallback mHost;
    FragmentController mController;
    FragmentContainer mContainer;
    Fragment mParent;

FragmentController:将Fragment的生命周期分发给FragmentManager.主要起消息传达作用,FragmentActivity将消息发到此处,Controller再发往FragmentManager。

FragmentTransaction:执行对Fragment的操作。主要方法有(只列出部分):

  • add:添加Fragment。
  • remove:移除一个已经存在的Fragment。
  • show:显示一个之前隐藏了的Fragment。
  • hide:隐藏已经存在的Fragment。
  • replace:此方法等于同时执行remove和add。
  • detach:移除Fragment的UI布局。此方法跟执行了addToBackStack的remove一样。
  • attach:重建Fragment的View并将其与原先执行了detach的Fragment连接。
  • commit:提交处理,这个提交不会立刻执行。它会被安排知道主线程已经准备好才开始执行。此方法只可以在Activity保存状态之前执行,不然会抛出IllegalStateException。如果想在状态保存后执行commit,应该使用commitAllowingStateLoss()。
  • commitAllowingStateLoss():允许在Activity状态保存后执行提交FragmentTransaction。
  • setTransition:选择一个Fragment的过渡动画。

BackStackRecord:继承了FragmentTransaction和实现了BackStackEntry,Runnable。BackStackEntry代表了Fragment的操作名称,他会在addToBackStack(String)之后才被创建。实际上BackStackRecord有一个双向链表:

static final class Op {
        Op next;
        Op prev;
        int cmd;
        Fragment fragment;
        int enterAnim;
        int exitAnim;
        int popEnterAnim;
        int popExitAnim;
        ArrayList<Fragment> removed;
    }

这个链表存储了所有的操作。这个双向链表的操作会在后面会讲解。


我们使用Fragment的操作流程一般是:

    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
    FirstFragment FirstFragment = new FirstFragment();
    fragmentTransaction.replace(R.id.relative_layout, firstFragment);
    fragmentTransaction.commit();

其实hide,show,detach,attach,remove的操作都大同小异,以remove举例:

public FragmentTransaction remove(Fragment fragment) {
        Op op = new Op();
        op.cmd = OP_REMOVE;
        op.fragment = fragment;
        addOp(op);

        return this;
    }

实例化Op,将状态添加进cmd,调用addOp:

void addOp(Op op) {
        if (mHead == null) {
            mHead = mTail = op;
        } else {
            op.prev = mTail;
            mTail.next = op;
            mTail = op;
        }
        op.enterAnim = mEnterAnim;
        op.exitAnim = mExitAnim;
        op.popEnterAnim = mPopEnterAnim;
        op.popExitAnim = mPopExitAnim;
        mNumOp++;
    }

将op添加进链表。

而replcae和add也差不多,只不过这两个先调用了doAddOp:

 private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
        fragment.mFragmentManager = mManager;

        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 (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;
        }

        Op op = new Op();
        op.cmd = opcmd;
        op.fragment = fragment;
        addOp(op);
    }

接下来我们看一下commit.commit其实和之前提到的commitAllowingStateLoss一样,都是调用了commitInternal(boolean allowStateLoss),只是commit时候的allowStateLoss是false,而另一个是true。
我们来看一下commitInternal:

 int commitInternal(boolean allowStateLoss) {
        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("  ", null, pw, null);
        }
        mCommitted = true;
        if (mAddToBackStack) {
            mIndex = mManager.allocBackStackIndex(this);
        } else {
            mIndex = -1;
        }
        mManager.enqueueAction(this, allowStateLoss);
        return mIndex;
    }

其实也没干啥,就是调用了FragmentManager的enqueueAction,这个方法将任务添加进等待执行的队伍中:

public void enqueueAction(Runnable action, boolean allowStateLoss) {
        if (!allowStateLoss) {
            checkStateLoss();
        }
        synchronized (this) {
            if (mDestroyed || mHost == null) {
                throw new IllegalStateException("Activity has been destroyed");
            }
            if (mPendingActions == null) {
                mPendingActions = new ArrayList<Runnable>();
            }
            mPendingActions.add(action);
            if (mPendingActions.size() == 1) {
                mHost.getHandler().removeCallbacks(mExecCommit);
                mHost.getHandler().post(mExecCommit);
            }
        }
    }

我们可以看到这里有一个方法checkStateLoss(),这个方法其实就是检查Activity的状态是否保存了,如果已经保存,就会抛出一个错误。很多人如果曾经在onSaveInstanceState等地方调用commit时一般会看到一个错误:Can not perform this action after onSaveInstanceState.其实就是在这里抛出的。方法最后调用了post。就是在这里把任务排入主线程的messageQueue中,直到主线程空闲下来才执行。而mExecCommit是什么来的的?

 Runnable mExecCommit = new Runnable() {
        @Override
        public void run() {
            execPendingActions();
        }
    };

我们继续看下去:

 public boolean execPendingActions() {
        if (mExecutingActions) {
            throw new IllegalStateException("Recursive entry to executePendingTransactions");
        }

        if (Looper.myLooper() != mHost.getHandler().getLooper()) {
            throw new IllegalStateException("Must be called from main thread of process");
        }

        boolean didSomething = false;

        while (true) {
            int numActions;

            synchronized (this) {
                if (mPendingActions == null || mPendingActions.size() == 0) {
                    break;
                }

                numActions = mPendingActions.size();
                if (mTmpActions == null || mTmpActions.length < numActions) {
                    mTmpActions = new Runnable[numActions];
                }
                mPendingActions.toArray(mTmpActions);
                mPendingActions.clear();
                mHost.getHandler().removeCallbacks(mExecCommit);
            }

            mExecutingActions = true;
            for (int i=0; i<numActions; i++) {
                mTmpActions[i].run();
                mTmpActions[i] = null;
            }
            mExecutingActions = false;
            didSomething = true;
        }

       .
       .
       .
        return didSomething;
    }

这里代码挺长,我们只看关键几个地方。这个方法必须在主线程中执行,他会把mPendingActions中的BackStackRecord(实现了Runnable接口哦)传入mTmpActions,然后调用它的run方法:

 public void run() {
        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Run: " + this);

        if (mAddToBackStack) {
            if (mIndex < 0) {
                throw new IllegalStateException("addToBackStack() called after commit()");
            }
        }

        bumpBackStackNesting(1);

        TransitionState state = null;
        SparseArray<Fragment> firstOutFragments = null;
        SparseArray<Fragment> lastInFragments = null;
        if (SUPPORTS_TRANSITIONS) {
            firstOutFragments = new SparseArray<Fragment>();
            lastInFragments = new SparseArray<Fragment>();

            calculateFragments(firstOutFragments, lastInFragments);

            state = beginTransition(firstOutFragments, lastInFragments, false);
        }

        int transitionStyle = state != null ? 0 : mTransitionStyle;
        int transition = state != null ? 0 : mTransition;
        Op op = mHead;
        while (op != null) {
            int enterAnim = state != null ? 0 : op.enterAnim;
            int exitAnim = state != null ? 0 : op.exitAnim;
            switch (op.cmd) {
                case OP_ADD: {
                    Fragment f = op.fragment;
                    f.mNextAnim = enterAnim;
                    mManager.addFragment(f, false);
                } break;
                case OP_REPLACE: {
                    Fragment f = op.fragment;
                    int containerId = f.mContainerId;
                    if (mManager.mAdded != null) {
                        for (int i=0; i<mManager.mAdded.size(); i++) {
                            Fragment old = mManager.mAdded.get(i);
                            if (FragmentManagerImpl.DEBUG) Log.v(TAG,
                                    "OP_REPLACE: adding=" + f + " old=" + old);
                            if (old.mContainerId == containerId) {
                                if (old == f) {
                                    op.fragment = f = null;
                                } else {
                                    if (op.removed == null) {
                                        op.removed = new ArrayList<Fragment>();
                                    }
                                    op.removed.add(old);
                                    old.mNextAnim = exitAnim;
                                    if (mAddToBackStack) {
                                        old.mBackStackNesting += 1;
                                        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
                                                + old + " to " + old.mBackStackNesting);
                                    }
                                    mManager.removeFragment(old, transition, transitionStyle);
                                }
                            }
                        }
                    }
                    if (f != null) {
                        f.mNextAnim = enterAnim;
                        mManager.addFragment(f, false);
                    }
                } break;
                case OP_REMOVE: {
                    Fragment f = op.fragment;
                    f.mNextAnim = exitAnim;
                    mManager.removeFragment(f, transition, transitionStyle);
                } break;
                case OP_HIDE: {
                    Fragment f = op.fragment;
                    f.mNextAnim = exitAnim;
                    mManager.hideFragment(f, transition, transitionStyle);
                } break;
                case OP_SHOW: {
                    Fragment f = op.fragment;
                    f.mNextAnim = enterAnim;
                    mManager.showFragment(f, transition, transitionStyle);
                } break;
                case OP_DETACH: {
                    Fragment f = op.fragment;
                    f.mNextAnim = exitAnim;
                    mManager.detachFragment(f, transition, transitionStyle);
                } break;
                case OP_ATTACH: {
                    Fragment f = op.fragment;
                    f.mNextAnim = enterAnim;
                    mManager.attachFragment(f, transition, transitionStyle);
                } break;
                default: {
                    throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
                }
            }

            op = op.next;
        }

        mManager.moveToState(mManager.mCurState, transition, transitionStyle, true);

        if (mAddToBackStack) {
            mManager.addBackStackState(this);
        }
    }

看晕了吧?其实就是根据op的cmd执行相应的操作:attachFrament,detachFragment,showFragment等等。代码的最后有一个mAddToBackStack。FragmentTransaction就是在这里判断是否添加进回退栈.

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

attachFragment,detachFragment等等最后会调用到moveToState,这个方法非常关键:

void moveToState(Fragment f, int newState, int transit, int transitionStyle,
            boolean keepActive) {
        // Fragments that are not currently added will sit in the onCreate() state.
        if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) {
            newState = Fragment.CREATED;
        }
        if (f.mRemoving && newState > f.mState) {
            // While removing a fragment, we can't change it to a higher state.
            newState = f.mState;
        }
        // Defer start if requested; don't allow it to move to STARTED or higher
        // if it's not already started.
        if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.STOPPED) {
            newState = Fragment.STOPPED;
        }
        if (f.mState < newState) {
            // For fragments that are created from a layout, when restoring from
            // state we don't want to allow them to be created until they are
            // being reloaded from the layout.
            if (f.mFromLayout && !f.mInLayout) {
                return;
            }  
            if (f.mAnimatingAway != null) {
                // The fragment is currently being animated...  but!  Now we
                // want to move our state back up.  Give up on waiting for the
                // animation, move to whatever the final state should be once
                // the animation is done, and then we can proceed from there.
                f.mAnimatingAway = null;
                moveToState(f, f.mStateAfterAnimating, 0, 0, true);
            }
            switch (f.mState) {
                case Fragment.INITIALIZING:
                    if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
                    if (f.mSavedFragmentState != null) {
                        f.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader());
                        f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(
                                FragmentManagerImpl.VIEW_STATE_TAG);
                        f.mTarget = getFragment(f.mSavedFragmentState,
                                FragmentManagerImpl.TARGET_STATE_TAG);
                        if (f.mTarget != null) {
                            f.mTargetRequestCode = f.mSavedFragmentState.getInt(
                                    FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
                        }
                        f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(
                                FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);
                        if (!f.mUserVisibleHint) {
                            f.mDeferStart = true;
                            if (newState > Fragment.STOPPED) {
                                newState = Fragment.STOPPED;
                            }
                        }
                    }
                    f.mHost = mHost;
                    f.mParentFragment = mParent;
                    f.mFragmentManager = mParent != null
                            ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();
                    f.mCalled = false;
                    f.onAttach(mHost.getContext());
                    if (!f.mCalled) {
                        throw new SuperNotCalledException("Fragment " + f
                                + " did not call through to super.onAttach()");
                    }
                    if (f.mParentFragment == null) {
                        mHost.onAttachFragment(f);
                    }

                    if (!f.mRetaining) {
                        f.performCreate(f.mSavedFragmentState);
                    }
                    f.mRetaining = false;
                    if (f.mFromLayout) {
                        // For fragments that are part of the content view
                        // layout, we need to instantiate the view immediately
                        // and the inflater will take care of adding it.
                        f.mView = f.performCreateView(f.getLayoutInflater(
                                f.mSavedFragmentState), null, f.mSavedFragmentState);
                        if (f.mView != null) {
                            f.mInnerView = f.mView;
                            if (Build.VERSION.SDK_INT >= 11) {
                                ViewCompat.setSaveFromParentEnabled(f.mView, false);
                            } else {
                                f.mView = NoSaveStateFrameLayout.wrap(f.mView);
                            }
                            if (f.mHidden) f.mView.setVisibility(View.GONE);
                            f.onViewCreated(f.mView, f.mSavedFragmentState);
                        } else {
                            f.mInnerView = null;
                        }
                    }
                 case Fragment.CREATED:
                    if (newState > Fragment.CREATED) {
                        if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
                        if (!f.mFromLayout) {
                            ViewGroup container = null;
                            .
                            .
                            .

其中,Fragment的onAttach,onCreate都是在Fragment.INITIALIZING中执行.onCreateView则要根据Fragment的布局是否从layout file中初始化。是则在Fragment.INITIALIZING中执行,否则是在Fragment.CREATED中执行。onActivityCreate是在Fragment.CREATED中执行。


以上是在FragmentActivity稳定下来后调用对Fragment进行的操作。下面继续探究我们在FragmentActivity的onCerate,onStart,onResume等生命周期中对Fragment进行操作

onCreate

 protected void onCreate(@Nullable Bundle savedInstanceState) {
        mFragments.attachHost(null /*parent*/);

        super.onCreate(savedInstanceState);

        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            mFragments.restoreLoaderNonConfig(nc.loaders);
        }
        if (savedInstanceState != null) {
            Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
            mFragments.restoreAllState(p, nc != null ? nc.fragments : null);
        }
        mFragments.dispatchCreate();
    }

attachHost是为了与FragmentManager建立连接,实质上就是调用了FragmentController的attachController,把HostCallback赋值给FragmentManager。接下来会通过getLastNonConfigurationInstance获取List,如果该Activity是异常恢复而来的话,这里会恢复LoaderManager(管理众多用于异步加载数据的Loader的类)和Fragment的状态。最后是mFragments.dispatchCreate,注意,这个方法会对mStateSaved赋值为false(还记得这个参数吗?用来判断Activity的状态是否保存/恢复)。所以在onCreate中队Fragment进行操作是安全的。
(ps:getLastNonConfigurationInstance只可以用来恢复不是因为Activity的configuration改变而导致的activity重建。具体保存的地方在onRetainNonConfigurationInstance(),这个方法在onStop和onDestroy间执行)

onStart

  protected void onStart() {
        super.onStart();

        mStopped = false;
        mReallyStopped = false;
        mHandler.removeMessages(MSG_REALLY_STOPPED);

        if (!mCreated) {
            mCreated = true;
            mFragments.dispatchActivityCreated();
        }

        mFragments.noteStateNotSaved();
        mFragments.execPendingActions();

        mFragments.doLoaderStart();

        // NOTE: HC onStart goes here.

        mFragments.dispatchStart();
        mFragments.reportLoaderStart();
    }

onStart和onCerate大同小异。这里注意,mFragments.noteStateNotSaved()最后调用了FragmentaManager的noteStateNotSaved,对mStateSaved赋值为false。还有一个关键的函数是mFragments.execPendingActions(),这是用来保障在执行了dispatchActivityCreated后,主线程没空执行FragmentManager的mPendingActions中的任务。

onResume

 protected void onResume() {
        super.onResume();
        mHandler.sendEmptyMessage(MSG_RESUME_PENDING);
        mResumed = true;
        mFragments.execPendingActions();
    }

这里可以看到,onResume并没有dispatchoResume,也没有对mStateSaves进行赋值,因此在这里对Fragment进行操作其实并不太安全,官方文档描述:

Dispatch onResume() to fragments. Note that for better inter-operation with older versions of the platform, at the point of this call the fragments attached to the activity are not resumed. This means that in some cases the previous state may still be saved, not allowing fragment transactions that modify the state. To correctly interact with fragments in their proper state, you should instead override onResumeFragments().

总的来说,就是当FragmentActivity onResume时,依附于FragmentActivity的Fragmen并没有resumed。因此要对Fragment进行操作最安全的地方是onResumeFragments。

onPause,onStop与上面都差不多,但这里要着重分析一下。
在Honeycomb版本中的Activity生命周期有了很大变化。在Honeycomb之前,activity在pause之前是不会被杀掉,但他会在onStop中被杀掉,这代表着onSaveInstanceState()会在onPause()之前被调用。从HoneyComb开始,Activity只会在stopped被杀掉,意味着onSaveInstanceState()现在会在onStop()之前被调用而不是在onPause()之前。因此commit在Honeycomb之前,在onPause和onStop之间调用会抛出错误。Honeycomb之后,在onStop之后调用会抛出错误。

最后大家可以看到,我们的分析都是基于FragmentActivity,也就是说,一旦你的Activity是并没有继承FragmentActivity,你就要考虑下在其他生命周期中调用commit是否会抛出错误了。举个例子,如果你继承的Activity并不是FragmentActivity(或是任何以FragmentActivity为基类的Activity),onActivityResult中调用commit是会报错的。

一句话总结:别在Activity和Fragment保存状态时(处于onSaveInstanceState),和恢复状态前执行commit()。也别在异步方法中(例如AsyncTask)中调用,因为你不能确定Activity的状态。

除非你使用commitAllowingStateLoss。

这里补充几个常用的方法

1、在FragmentTransaction调用了addToBackStack了,按后退键就可以看到上一次的保存的Fragment。而很多时候,根据需求,我们需要在标题栏左边加一个后退键。那我们要在那里调用什么函数呢?我们看一下onBackPressed的源码:

 public void onBackPressed() {
        if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) {
            supportFinishAfterTransition();
        }
    }

可以发现这里其实是调用了popBackStackImmediate()。

2、当我们在某个Activity中使用了Fragment,然后按home键或使用了分享之类的功能而长时间没有返回时,fragment的引用可能会被销毁,但是实例在内存中还存在。这时候如果我们相对这个fragment进行操作就会发现,无法remove/hide这个fragment。所以我们可以在一开始加载这个fragment的时候就调用

fragmentTransaction.add(R.id.relative_layout ,thirdFragment, "ThirdFragment");

在想调用thirdfragment时先进行非空判断,再调用这个方法

thirdFragment = (ThirdFragment)fragmentManager.findFragmentByTag("ThirdFragment");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值