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

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 mBackStack; 每次remove 的时候都是remove 掉最后一个。Fragment 默认是没有保存栈信息的。

// FragmentManagerImpl.java
addBackStackState(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.java
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.

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

  • add 流程。

1.获取FragmentTransaction。即BackStackRecord

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

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

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

3.doAddOp 操作。

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

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

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

5.enqueueAction 操作。

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

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

// FragmentManagerImpl.java
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 就是上面的mPendingActions,通过toArray 方法转化成了一个零时数组
            mPendingActions.toArray(mTmpActions); 
            mPendingActions.clear();
            mHost.getHandler().removeCallbacks(mExecCommit);
        }

        mExecutingActions = true;
        for (int i=0; i// 执行零时数组的run 方法,其实就是BackStackRecord 的run 方法。
            mTmpActions[i].run(); 
            mTmpActions[i] = null;
        }
        mExecutingActions = false;
        didSomething = true;
    }

    if (mHavePendingDeferredStart) {
        boolean loadersRunning = false;
        for (int i=0; iif (f != null && f.mLoaderManager != null) {
                loadersRunning |= f.mLoaderManager.hasRunningLoaders();
            }
        }
        if (!loadersRunning) {
            mHavePendingDeferredStart = false;
            startPendingDeferredFragments();
        }
    }
    return didSomething;
}

7.执行BackStateRecord 的run 方法。

// BackStackRecord.java
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 firstOutFragments = null;
    SparseArray lastInFragments = null;
    if (SUPPORTS_TRANSITIONS) { // 支持过度动画的一些操作
        firstOutFragments = new SparseArray();
        lastInFragments = new SparseArray();

        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.java
public void addFragment(Fragment fragment, boolean moveToStateNow) {
    if (mAdded == null) {
        mAdded = new ArrayList();
    }
    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.java
void 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; iif (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全解析系列

你可能感兴趣的:(Android)