Android Fragment解析以及Fragment中碰到的各种坑

来源:互联网 发布:天下粮仓 知乎 编辑:程序博客网 时间:2024/05/21 10:30

Fragment解决的问题

  1. 碎片化。解决了什么碎片化?
  2. 更加小巧了。相比于Activity 操作Fragment 更加的灵活?

前置知识

  1. Activity 生命周期。Fragment的生命周期会跟随Activity的生命周期所以需要知道Activity的生命周期。

  2. Activity 中onSaveInstanceState 和onRestoreInstanceState(即Activity的备忘录模式)。Activity 在某种情况下会备份一些信息,包括管理的Dialog、里面的Fragment、Activity 对应的Window 中对应的View 信息等。Fragment 也需要备份一些自身的信息。所以这一块也需要了解一点好。

  3. Activity 任务栈。TaskRecord.java

Fragment 栈

Fragment 可以保存自己的栈信息。在FramgentManagerImpl 中维护了一个Fragment 的栈信息ArrayList<BackStackRecord> mBackStack; 每次remove 的时候都是remove 掉最后一个。Fragment 默认是没有保存栈信息的。

// FragmentManagerImpl.javaaddBackStackState(BackStackRecord state) popBackStack() // 有一系列的方法,最终都是会调用popBackStackState()

流程

一般的操作是这样的getSupportFragmentManager().beginTransaction().add/replace/remove/hide/show/detach/attach.commit()。那么这样的操作是如何将一个View(一般写在Activity 对应的xml 中) 和一个Fragment 联系在一起的呢?

首先需要了解一个问题,FragmentManagerImpl 中保存了一个状态信息mCurState(Activity 生命周期会对其影响) 同时Fragment 中也保存了一个状态信息mState。FragmentManagerImpl 中保存的是当前的状态信息,而不同的Fragment中则保存的各自的状态信息,最终的结果是将Fragment 的状态变成FragmentManagerImpl 的状态。

Fragment 的几种不同状态,默认是INITIALIZING

// Fragment.javastatic 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.

介绍其中的几个流程,代码是基于support-v4-23.1.1 的。

  • add 流程。

1.获取FragmentTransaction。即BackStackRecord

@Overridepublic FragmentTransaction beginTransaction() {    return new BackStackRecord(this);}

2.add 操作,这里使用了某一个add 方法(有多个),很简单只是做了一个doAddOp 操作。

// BackStackRecord.javapublic FragmentTransaction add(int containerViewId, Fragment fragment) {    doAddOp(containerViewId, fragment, null, OP_ADD);    return this;}

3.doAddOp 操作。

// BackStackRecord.javaprivate 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); // Op 相当于是一个双向列表,这里是在列表中加入一个一些Op 对象。}

4.commit 操作会执行到commitInternal,直接看commitInternal。

// BackStackRecord.javaint 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); // 第一个参数是this,也就是BackStackRecord 的对象。    return mIndex;}

5.enqueueAction 操作。

// FragmentManagerImpl.javapublic 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>();        }        // 将action(BackStackRecord) 加入到mPendingActions        mPendingActions.add(action);         if (mPendingActions.size() == 1) {            mHost.getHandler().removeCallbacks(mExecCommit);            mHost.getHandler().post(mExecCommit); //         }    }}

6.执行mExecCommit 中的run 方法,就是execPendingActions 方法。

// FragmentManagerImpl.javapublic 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 就是上面的mPendingActions,通过toArray 方法转化成了一个零时数组            mPendingActions.toArray(mTmpActions);             mPendingActions.clear();            mHost.getHandler().removeCallbacks(mExecCommit);        }        mExecutingActions = true;        for (int i=0; i<numActions; i++) {            // 执行零时数组的run 方法,其实就是BackStackRecord 的run 方法。            mTmpActions[i].run();             mTmpActions[i] = null;        }        mExecutingActions = false;        didSomething = true;    }    if (mHavePendingDeferredStart) {        boolean loadersRunning = false;        for (int i=0; i<mActive.size(); i++) {            Fragment f = mActive.get(i);            if (f != null && f.mLoaderManager != null) {                loadersRunning |= f.mLoaderManager.hasRunningLoaders();            }        }        if (!loadersRunning) {            mHavePendingDeferredStart = false;            startPendingDeferredFragments();        }    }    return didSomething;}

7.执行BackStateRecord 的run 方法。

// BackStackRecord.javapublic 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: {                 // 最终执行到的地方,会执行addFragment 操作                Fragment f = op.fragment;                f.mNextAnim = enterAnim;                mManager.addFragment(f, false);            } break;            ... // 省略很多代码            default: {                throw new IllegalArgumentException("Unknown cmd: " + op.cmd);            }        }        op = op.next;    }    // 执行完相应的操作以后会执行moveToState 操作    mManager.moveToState(mManager.mCurState, transition, transitionStyle, true);    if (mAddToBackStack) {        mManager.addBackStackState(this);    }}

8.上面注释已经有分析需要的做操作,首先是FragmentManageImpl 的addFragment 操作。

// FragmentManagerImpl.javapublic void addFragment(Fragment fragment, boolean moveToStateNow) {    if (mAdded == null) {        mAdded = new ArrayList<Fragment>();    }    if (DEBUG) Log.v(TAG, "add: " + fragment);    // 将Fragment 置为Active 状态。    makeActive(fragment);     if (!fragment.mDetached) {        if (mAdded.contains(fragment)) {            throw new IllegalStateException("Fragment already added: " + fragment);        }        // 将该Fragment 加入到mAdded List中        mAdded.add(fragment);         fragment.mAdded = true;        fragment.mRemoving = false;        if (fragment.mHasMenu && fragment.mMenuVisible) {            mNeedMenuInvalidate = true;        }        if (moveToStateNow) { // 这里一般是false            moveToState(fragment);        }    }}

执行FragmentManageImpl 的moveToState。

// FragmentManagerImpl.javavoid moveToState(int newState, int transit, int transitStyle, boolean always) {    if (mHost == null && newState != Fragment.INITIALIZING) {        throw new IllegalStateException("No host");    }    if (!always && mCurState == newState) {        return;    }    mCurState = newState;    if (mActive != null) {        boolean loadersRunning = false;        for (int i=0; i<mActive.size(); i++) {            Fragment f = mActive.get(i);            if (f != null) {                // 执行下一步moveToState,注意这里是active 的Fragment 才会继续向下执行的                moveToState(f, newState, transit, transitStyle, false);                if (f.mLoaderManager != null) {                    loadersRunning |= f.mLoaderManager.hasRunningLoaders();                }            }        }        ... //  省略代码    }}

执行FragmentManageImpl 下一步的moveToState(存在Fragment 的时候才会执行)。这段代码里面一个重要的逻辑是Fragment 的状态和FragmentManagerImpl 状态的比较,FragmentManagerImpl 的状态是Fragment 将要到达的状态。

void moveToState(Fragment f, int newState, int transit, int transitionStyle,        boolean keepActive) {    if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) {        newState = Fragment.CREATED;    }    if (f.mRemoving && newState > f.mState) {        newState = f.mState;    }    if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.STOPPED) {        newState = Fragment.STOPPED;    }    if (f.mState < newState) {         // Fragment 的状态和FragmentManagerImpl 的状态比较,上面有解释。        if (f.mFromLayout && !f.mInLayout) {            return;        }          if (f.mAnimatingAway != null) {            f.mAnimatingAway = null;            moveToState(f, f.mStateAfterAnimating, 0, 0, true);        }        // 注意case 里面没有break        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;                // 执行Fragment 的onAttach 方法                f.onAttach(mHost.getContext());                 if (!f.mCalled) {                    throw new SuperNotCalledException("Fragment " + f                            + " did not call through to super.onAttach()");                }                if (f.mParentFragment == null) {                    // 最终毁掉到Activity 的onAtachFragment                    mHost.onAttachFragment(f);                 }                if (!f.mRetaining) {                    // 最终执行Fragment 的onCreate                    f.performCreate(f.mSavedFragmentState);                 }                f.mRetaining = false;                if (f.mFromLayout) {                    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;                        if (f.mContainerId != 0) {                            // f.mContainerId 就是通过设置BackStackRecord.add() 时候传过来的id,即container 就是xml 文件中定义的View。而onFindViewById 最终会调用到Activity 的findViewById。                            container = (ViewGroup)mContainer.onFindViewById(f.mContainerId);                            if (container == null && !f.mRestored) {                                throwException(new IllegalArgumentException(                                        "No view found for id 0x"                                        + Integer.toHexString(f.mContainerId) + " ("                                        + f.getResources().getResourceName(f.mContainerId)                                        + ") for fragment " + f));                            }                        }                        f.mContainer = container;                        // 最终会调用到Fragment 的onCreateView()                        f.mView = f.performCreateView(f.getLayoutInflater(                                f.mSavedFragmentState), container, 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 (container != null) {                                Animation anim = loadAnimation(f, transit, true,                                        transitionStyle);                                if (anim != null) {                                    setHWLayerAnimListenerIfAlpha(f.mView, anim);                                    f.mView.startAnimation(anim);                                }                                // container 将Fragment onCreateView 得到的View 加进去,这样相当于建立了Fragment 与View 之间的联系。                                container.addView(f.mView);                            }                            if (f.mHidden) f.mView.setVisibility(View.GONE);                            // Fragment 的onViewCreated 回调。                            f.onViewCreated(f.mView, f.mSavedFragmentState);                        } else {                            f.mInnerView = null;                        }                    }                    // 最终回调到onActivityCreated                    f.performActivityCreated(f.mSavedFragmentState);                    if (f.mView != null) {                        f.restoreViewState(f.mSavedFragmentState);                    }                    f.mSavedFragmentState = null;                }            case Fragment.ACTIVITY_CREATED:            case Fragment.STOPPED:                if (newState > Fragment.STOPPED) {                    if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);                    // 最终执行到Fragment onStart                    f.performStart();                }            case Fragment.STARTED:                if (newState > Fragment.STARTED) {                    if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);                    f.mResumed = true;                    // 最终执行到Fragment onResume                    f.performResume();                    f.mSavedFragmentState = null;                    f.mSavedViewState = null;                }        }    } else if (f.mState > newState) {        ... // 省略很多代码    }       f.mState = newState;}
  • show 流程。

和add 的操作多重合,其中2,3会替换成相应的show 操作,然后关键看第7步的执行,show 会执行到FragmentManagerImpl 的showFragment 方法。从下面的代码中可以看出showFragment 是不会执行moveToState 方法的,而仅仅通过fragment.mView.setVisibility(View.VISIBLE); 将Fragment 中的mView 设置为可见(同理可得hideFragment 则是将mView 设置为不可见)。

public void showFragment(Fragment fragment, int transition, int transitionStyle) {    if (DEBUG) Log.v(TAG, "show: " + fragment);    if (fragment.mHidden) {        fragment.mHidden = false;        if (fragment.mView != null) {            Animation anim = loadAnimation(fragment, transition, true,                    transitionStyle);            if (anim != null) {                setHWLayerAnimListenerIfAlpha(fragment.mView, anim);                fragment.mView.startAnimation(anim);            }            // 设置为可见            fragment.mView.setVisibility(View.VISIBLE);        }        if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {            mNeedMenuInvalidate = true;        }        fragment.onHiddenChanged(false);    }}

思考问题

  • replace 和show/hide 的区别?

replace 的操作相当于执行了addFragment + removeFragment操作,结果对应的是一个Fragment 生命周期的结束以及另一个Fragment 生命周期的开始。而show/hide 则只是对Fragment 中的View 进行了VISIBLE/GONE操作。所有总结一下区别。

1.相对而言show/hide 方法会比较快。

2.replace 占用的内存可能比较少。show/hide 其实会将多个Fragment 一直保存着,而replace 时当内存不足会销毁被replace 的Fragment。

常见异常信息

Fragment 中有一些坑,平常使用时好好的,放线上就会出现大量的crash,这也是因为对Fragment 的了解不够(不过真心还是有一些坑)。

  • java.lang.IllegalStateException: Activity has been destroyed

这个异常发生在FragmentManagerImpl.enqueueAction 中,从下面的代码中可以得出来是因为Activity 执行了onDestory,一般出现情况是在请求完一个接口才去执行commit 操作,而此时home 回去Activity 执行了onDestroy 操作。但是Activity.isDestroyed()需要在api 17以上调用,所有对于耗时操作以后才展示的Fragment 可以通过在Activity.onDestroy中添加标示字段标示destroy 了或者直接try catch 掉。

if (mDestroyed || mHost == null) {    throw new IllegalStateException("Activity has been destroyed");}
  • java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

这个异常发生在FragmentManagerImpl.checkStateLoss 中,mStateSaved置为true是在Activity.onSaveInstanceState或者FragmentManagerImpl.dispatchStop(执行到dispatchStop意味着会执行Activity.onStop,这个之前一定执行了Activity.onSaveInstanceState)之后。所以意思是当Activity 保存自身状态以后是不能在处理Fragment 的操作了。终其原因是Fragment 也包含了一些状态信息,Activity 保存状态信息的时候Fragment 也会执行保存状态信息的操作,如果保存了以后继续处理Fragment 状态信息变化的操作,新的状态信息就无法保留,所以抛出异常。一般出现情况是在请求完一个接口才去执行commit 操作,解决办法可以替换commit 操作为commitAllowingStateLoss 操作就行,commitAllowingStateLoss 不会去执行checkStateLoss

if (mStateSaved) {    throw new IllegalStateException(            "Can not perform this action after onSaveInstanceState");}

有一个特殊的情况,如下栈信息。原因和上面的分析其实是一样的,也就是onBackPressedActivity.onSaveInstanceState之后才执行的,而onBackPressed一般是在用户退出按钮的时候才会执行。也就是这个异常出现的情况是在一个Activity可见的情况下触发了Activity.onSaveInstanceState然后点击了Back 按钮。因为所有的Activity都是继承了一个封装的BaseActivity,所以最后通过try catch解决的。

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState    at android.app.FragmentManagerImpl.checkStateLoss(FragmentManagerImpl.java:1413)    at android.app.FragmentManagerImpl.popBackStackImmediate(FragmentManagerImpl.java:576)    at android.app.Activity.onBackPressed(Activity.java:2520)
  • java.lang.IllegalStateException: Fragment no longer exists for key

这个异常发生在FragmentManagerImpl.getFragment 中,一般发生在ViewPager.setDapter,栈信息如下。

java.lang.IllegalStateException: Fragment no longer exists for key f2: index 2    at android.support.v4.app.FragmentManagerImpl.getFragment(FragmentManagerImpl.java)    at android.support.v4.app.FragmentStatePagerAdapter.restoreState(FragmentStatePagerAdapter.java)    at android.support.v4.view.ViewPager.setAdapter(ViewPager.java)

分析可得restoreState只有在ViewPager.onRestoreInstanceState之后才执行。同时也可以分析出第一次执行setAdapter不会执行到restoreState。java lang illegalstateexception fragement no longer exists for key f1 index 3 这里给了一个解决方法。但是这样会导致ViewPageronRestoreInstanceState恢复过来的时候将无法恢复了,即home 回去以后Activity.onDestroy执行,再次打开Activtiy 无法恢复。所以目前的解决办法是try catch 了。

@Override public Parcelable saveState() {    return null; } 

总结

Fragment 解决了碎片化问题?可以假设Activity 分成了上下两部分,使用两个View 分别展示在上面和下面,然后对两个View 进行各种不一样的操作也可以。那么为什么还需要使用Fragment。在View 中进行一些复杂的逻辑,比如网络请求等等会导致耦合度非常的高,同时对于Activity 的生命周期一些特殊逻辑也不是很好处理。Fragment 最终也是展示了一个View,但是封装出来,同时对应了Activity 的生命周期,代码的冗余也会减少很多。

打开一个新的Activity 的时候,需要做的操作可能包含了Window 的创建等等,相对来说需要的资源会更多,而使用Fragment 则不需要,这样在速度方面可能也比较的快。

关于Fragment 的坑也有一些,上面常见的异常信息里面列举了非常常见的几个,当需要延后打开一个Fragment 的时候,比如请求完接口在展示的时候,就需要考虑使用commitAllowingStateLoss以及考虑Activity.onDestroy已经执行了的情况。个人觉得更好的应该是在Activity 做较少的操作,直接在Activity.onCreate的时候直接执行commit,在Fragment 中去做相应的逻辑。

推荐

fragment transaction commit state loss
advocating against android fragments
Fragment全解析系列

1 0