Android进阶——Fragment详解之操作原理

Posted CrazyMo_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android进阶——Fragment详解之操作原理相关的知识,希望对你有一定的参考价值。

引言

前一篇文章总结了Fragment 的基本概念和基本的用法,相信大家也能够掌握一些知识了,但是对于一些操作可能还是不知其所以然,说实话曾经很长一段时间为也是晕乎乎的,后来才慢慢重视去学习了解,才略知一二,遂分享之。

一、管理Fragement所涉及到的关键类

应用Fragment的过程中涉及到的关键类主要有:FragmentManager和、FragmentManagerImplFragmentTransaction和BackStackRecord等。

二、Fragment操作原理详述

1、FragmentManager和FragmentManagerImpl

FragmentManager是一个抽象类,定了获取指定Fragement对象findFragmentById(id)findFragmentByTag(tag)、从后台栈中弹出(模拟用户按下BACK键)popBackStack(),注册监听addOnBackStackChangeListner和移除监听removeOnBackStackChangedListener以及开启事务的方法beginTransaction,但直接与Activity交互的并承担实际Fragment管理工作的是FragmentManagerImpl。他们的部分源码结构【FragmentManager.java (frameworks\base\core\java\android\app)
】如下:

public abstract class FragmentManager {

    public interface BackStackEntry {
        public int getId();
        public String getName();
        ...
    }
    /**
     * Interface to watch for changes to the back stack.
     */
    public interface OnBackStackChangedListener {
        /**
         * Called whenever the contents of the back stack change.
         */
        public void onBackStackChanged();
    }

    public abstract FragmentTransaction beginTransaction();
    public abstract Fragment findFragmentById(int id);
    public abstract Fragment findFragmentByTag(String tag);
    public static final int POP_BACK_STACK_INCLUSIVE = 1<<0;
    public abstract void popBackStack();
    public abstract void popBackStack(String name, int flags);
    public abstract void popBackStack(int id, int flags);
    public abstract void addOnBackStackChangedListener(OnBackStackChangedListener listener);
    public abstract void removeOnBackStackChangedListener(OnBackStackChangedListener listener);
    public abstract Fragment getFragment(Bundle bundle, String key);
    public abstract Fragment.SavedState saveFragmentInstanceState(Fragment f);
}

/**
 * Container for fragments associated with an activity.
 */
final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 {
    ArrayList<BackStackRecord> mBackStackIndices;//BackStackRecord实现了FragmentTransaction
    static class AnimateOnHWLayerIfNeededListener implements Animator.AnimatorListener {
        private boolean mShouldRunOnHWLayer = false;
        private View mView;
        public AnimateOnHWLayerIfNeededListener(final View v) {
            if (v == null) {
                return;
            }
            mView = v;
        }

        @Override
        public void onAnimationStart(Animator animation) {
            mShouldRunOnHWLayer = shouldRunOnHWLayer(mView, animation);
            if (mShouldRunOnHWLayer) {
                mView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
            }
        }

        @Override
        public void onAnimationEnd(Animator animation) {
            if (mShouldRunOnHWLayer) {
                mView.setLayerType(View.LAYER_TYPE_NONE, null);
            }
            mView = null;
            animation.removeListener(this);
        }

        @Override
        public void onAnimationCancel(Animator animation) {

        }

        @Override
        public void onAnimationRepeat(Animator animation) {

        }
    }
    ...
    @Override
    public FragmentTransaction beginTransaction() {
        return new BackStackRecord(this);
    }
    @Override
    public void popBackStack() {
        enqueueAction(new Runnable() {
            @Override public void run() {
                popBackStackState(mHost.getHandler(), null, -1, 0);
            }
        }, false);
    }
    @Override
    public void popBackStack(final String name, final int flags) {
        enqueueAction(new Runnable() {
            @Override public void run() {
                popBackStackState(mHost.getHandler(), name, -1, flags);
            }
        }, false);
    }
    @Override
    public void addOnBackStackChangedListener(OnBackStackChangedListener listener) {
        if (mBackStackChangeListeners == null) {
            mBackStackChangeListeners = new ArrayList<OnBackStackChangedListener>();
        }
        mBackStackChangeListeners.add(listener);
    }

    @Override
    public void removeOnBackStackChangedListener(OnBackStackChangedListener listener) {
        if (mBackStackChangeListeners != null) {
            mBackStackChangeListeners.remove(listener);
        }
    }
    Animator loadAnimator(Fragment fragment, int transit, boolean enter,
            int transitionStyle) {
        Animator animObj = fragment.onCreateAnimator(transit, enter,
                fragment.mNextAnim);
        if (animObj != null) {
            return animObj;
        }

        if (fragment.mNextAnim != 0) {
            Animator anim = AnimatorInflater.loadAnimator(mHost.getContext(), fragment.mNextAnim);
            if (anim != null) {
                return anim;
            }
        }

        if (transit == 0) {
            return null;
        }

        int styleIndex = transitToStyleIndex(transit, enter);
        if (styleIndex < 0) {
            return null;
        }

        if (transitionStyle == 0 && mHost.onHasWindowAnimations()) {
            transitionStyle = mHost.onGetWindowAnimations();
        }
        if (transitionStyle == 0) {
            return null;
        }

        TypedArray attrs = mHost.getContext().obtainStyledAttributes(transitionStyle,
                com.android.internal.R.styleable.FragmentAnimation);
        int anim = attrs.getResourceId(styleIndex, 0);
        attrs.recycle();

        if (anim == 0) {
            return null;
        }

        return AnimatorInflater.loadAnimator(mHost.getContext(), anim);
    }
    public void addFragment(Fragment fragment, boolean moveToStateNow) {
        if (mAdded == null) {
            mAdded = new ArrayList<Fragment>();
        }
        if (DEBUG) Log.v(TAG, "add: " + fragment);
        makeActive(fragment);
        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);
            }
        }
    }

    public void removeFragment(Fragment fragment, int transition, int transitionStyle) {
        if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
        final boolean inactive = !fragment.isInBackStack();
        if (!fragment.mDetached || inactive) {
            if (false) {
                // Would be nice to catch a bad remove here, but we need
                // time to test this to make sure we aren‘t crashes cases
                // where it is not a problem.
                if (!mAdded.contains(fragment)) {
                    throw new IllegalStateException("Fragment not added: " + fragment);
                }
            }
            if (mAdded != null) {
                mAdded.remove(fragment);
            }
            if (fragment.mHasMenu && fragment.mMenuVisible) {
                mNeedMenuInvalidate = true;
            }
            fragment.mAdded = false;
            fragment.mRemoving = true;
            moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,
                    transition, transitionStyle, false);
        }
    }

    public void hideFragment(Fragment fragment, int transition, int transitionStyle) {
        if (DEBUG) Log.v(TAG, "hide: " + fragment);
        if (!fragment.mHidden) {
            // If there is a showing or hidding animation, stop it immediately.
            if (fragment.mAnimatingShowHide != null) {
                fragment.mAnimatingShowHide.end();
            }

            fragment.mHidden = true;
            if (fragment.mView != null) {
                Animator anim = loadAnimator(fragment, transition, false,
                        transitionStyle);
                if (anim != null) {
                    fragment.mAnimatingShowHide = anim;
                    anim.setTarget(fragment.mView);
                    // Delay the actual hide operation until the animation finishes, otherwise
                    // the fragment will just immediately disappear
                    final Fragment finalFragment = fragment;
                    anim.addListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            finalFragment.mAnimatingShowHide = null;
                            if (finalFragment.mView != null) {
                                finalFragment.mView.setVisibility(View.GONE);
                            }
                        }
                    });
                    setHWLayerAnimListenerIfAlpha(finalFragment.mView, anim);
                    anim.start();
                } else {
                    fragment.mView.setVisibility(View.GONE);
                }
            }
            if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
                mNeedMenuInvalidate = true;
            }
            fragment.onHiddenChanged(true);
        }
    }

    public void showFragment(Fragment fragment, int transition, int transitionStyle) {
        if (DEBUG) Log.v(TAG, "show: " + fragment);
        if (fragment.mHidden) {
            // If there is a showing or hidding animation, stop it immediately.
            if (fragment.mAnimatingShowHide != null) {
                fragment.mAnimatingShowHide.end();
            }

            fragment.mHidden = false;
            if (fragment.mView != null) {
                Animator anim = loadAnimator(fragment, transition, true,
                        transitionStyle);
                if (anim != null) {
                    fragment.mAnimatingShowHide = anim;
                    anim.setTarget(fragment.mView);
                    setHWLayerAnimListenerIfAlpha(fragment.mView, anim);

                    final Fragment finalFragment = fragment;
                    anim.addListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            finalFragment.mAnimatingShowHide = null;
                        }
                    });

                    anim.start();
                }
                fragment.mView.setVisibility(View.VISIBLE);
            }
            if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
                mNeedMenuInvalidate = true;
            }
            fragment.onHiddenChanged(false);
        }
    }

    public void detachFragment(Fragment fragment, int transition, int transitionStyle) {
        if (DEBUG) Log.v(TAG, "detach: " + fragment);
        if (!fragment.mDetached) {
            fragment.mDetached = true;
            if (fragment.mAdded) {
                // We are not already in back stack, so need to remove the fragment.
                if (mAdded != null) {
                    if (DEBUG) Log.v(TAG, "remove from detach: " + fragment);
                    mAdded.remove(fragment);
                }
                if (fragment.mHasMenu && fragment.mMenuVisible) {
                    mNeedMenuInvalidate = true;
                }
                fragment.mAdded = false;
                moveToState(fragment, Fragment.CREATED, transition, transitionStyle, false);
            }
        }
    }

    public void attachFragment(Fragment fragment, int transition, int transitionStyle) {
        if (DEBUG) Log.v(TAG, "attach: " + fragment);
        if (fragment.mDetached) {
            fragment.mDetached = false;
            if (!fragment.mAdded) {
                if (mAdded == null) {
                    mAdded = new ArrayList<Fragment>();
                }
                if (mAdded.contains(fragment)) {
                    throw new IllegalStateException("Fragment already added: " + fragment);
                }
                if (DEBUG) Log.v(TAG, "add from attach: " + fragment);
                mAdded.add(fragment);
                fragment.mAdded = true;
                if (fragment.mHasMenu && fragment.mMenuVisible) {
                    mNeedMenuInvalidate = true;
                }
                moveToState(fragment, mCurState, transition, transitionStyle, false);
            }
        }
    }

    public Fragment findFragmentById(int id) {
        if (mAdded != null) {
            // First look through added fragments.
            for (int i=mAdded.size()-1; i>=0; i--) {
                Fragment f = mAdded.get(i);
                if (f != null && f.mFragmentId == id) {
                    return f;
                }
            }
        }
        if (mActive != null) {
            // Now for any known fragment.
            for (int i=mActive.size()-1; i>=0; i--) {
                Fragment f = mActive.get(i);
                if (f != null && f.mFragmentId == id) {
                    return f;
                }
            }
        }
        return null;
    }

    public Fragment findFragmentByTag(String tag) {
        if (mAdded != null && tag != null) {
            // First look through added fragments.
            for (int i=mAdded.size()-1; i>=0; i--) {
                Fragment f = mAdded.get(i);
                if (f != null && tag.equals(f.mTag)) {
                    return f;
                }
            }
        }
        if (mActive != null && tag != null) {
            // Now for any known fragment.
            for (int i=mActive.size()-1; i>=0; i--) {
                Fragment f = mActive.get(i);
                if (f != null && tag.equals(f.mTag)) {
                    return f;
                }
            }
        }
        return null;
    }
    void reportBackStackChanged() {
        if (mBackStackChangeListeners != null) {
            for (int i=0; i<mBackStackChangeListeners.size(); i++) {
                mBackStackChangeListeners.get(i).onBackStackChanged();
            }
        }
    }

    void addBackStackState(BackStackRecord state) {
        if (mBackStack == null) {
            mBackStack = new ArrayList<BackStackRecord>();
        }
        mBackStack.add(state);
        reportBackStackChanged();
    }
    @Override
    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
        if (!"fragment".equals(name)) {
            return null;
        }

        String fname = attrs.getAttributeValue(null, "class");
        TypedArray a =
                context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Fragment);
        if (fname == null) {
            fname = a.getString(com.android.internal.R.styleable.Fragment_name);
        }
        int id = a.getResourceId(com.android.internal.R.styleable.Fragment_id, View.NO_ID);
        String tag = a.getString(com.android.internal.R.styleable.Fragment_tag);
        a.recycle();

        int containerId = parent != null ? parent.getId() : 0;
        if (containerId == View.NO_ID && id == View.NO_ID && tag == null) {
            throw new IllegalArgumentException(attrs.getPositionDescription()
                    + ": Must specify unique android:id, android:tag, or have a parent with"
                    + " an id for " + fname);
        }

        // If we restored from a previous state, we may already have
        // instantiated this fragment from the state and should use
        // that instance instead of making a new one.
        Fragment fragment = id != View.NO_ID ? findFragmentById(id) : null;
        if (fragment == null && tag != null) {
            fragment = findFragmentByTag(tag);
        }
        if (fragment == null && containerId != View.NO_ID) {
            fragment = findFragmentById(containerId);
        }

        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "onCreateView: id=0x"
                + Integer.toHexString(id) + " fname=" + fname
                + " existing=" + fragment);
        if (fragment == null) {
            fragment = Fragment.instantiate(context, fname);
            fragment.mFromLayout = true;
            fragment.mFragmentId = id != 0 ? id : containerId;
            fragment.mContainerId = containerId;
            fragment.mTag = tag;
            fragment.mInLayout = true;
            fragment.mFragmentManager = this;
            fragment.mHost = mHost;
            fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
            addFragment(fragment, true);
        } else if (fragment.mInLayout) {
            // A fragment already exists and it is not one we restored from
            // previous state.
            throw new IllegalArgumentException(attrs.getPositionDescription()
                    + ": Duplicate id 0x" + Integer.toHexString(id)
                    + ", tag " + tag + ", or parent id 0x" + Integer.toHexString(containerId)
                    + " with another fragment for " + fname);
        } else {
            // This fragment was retained from a previous instance; get it
            // going now.
            fragment.mInLayout = true;
            // If this fragment is newly instantiated (either right now, or
            // from last saved state), then give it the attributes to
            // initialize itself.
            if (!fragment.mRetaining) {
                fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
            }
        }

        // If we haven‘t finished entering the CREATED state ourselves yet,
        // push the inflated child fragment along.
        if (mCurState < Fragment.CREATED && fragment.mFromLayout) {
            moveToState(fragment, Fragment.CREATED, 0, 0, false);
        } else {
            moveToState(fragment);
        }

        if (fragment.mView == null) {
            throw new IllegalStateException("Fragment " + fname
                    + " did not create a view.");
        }
        if (id != 0) {
            fragment.mView.setId(id);
        }
        if (fragment.mView.getTag() == null) {
            fragment.mView.setTag(tag);
        }
        return fragment.mView;
    }

    @Override
    public View onCreateView(String name, Context context, AttributeSet attrs) {
        return null;
    }

    LayoutInflater.Factory2 getLayoutInflaterFactory() {
        return this;
    }
}

2、FragmentTransaction和BackStackRecord

Android对于Fragment的操作管理,不是针对于某一次的操作,而是类似于Git记录的是一次改变或者像数据库的事务一般,记录的是一系列的add、replace、remove操作集合(这些add等操作都最后都会封装到一个Op对象里,Op对象可以看成一个双向链表,记录了前一个操作和后一个操作,比如说我们add了N个MainFragment后,这N个操作会由系统封装成N个Op并存到这个对应的BackStackRecord),FragmentTransaction和BackStackRecord(实际是一个实现了FragmentTransaction的类)则是用于管理Fragment事务的业务类,部分源码结构如下:

final class BackStackState implements Parcelable {
    public BackStackState(FragmentManagerImpl fm, BackStackRecord bse) {
        int numRemoved = 0;
        BackStackRecord.Op op = bse.mHead;
        while (op != null) {
            if (op.removed != null) {
                numRemoved += op.removed.size();
            }
            op = op.next;
        }
        mOps = new int[bse.mNumOp * 7 + numRemoved];

        if (!bse.mAddToBackStack) {
            throw new IllegalStateException("Not on back stack");
        }

        op = bse.mHead;
        int pos = 0;
        while (op != null) {
            mOps[pos++] = op.cmd;
            mOps[pos++] = op.fragment != null ? op.fragment.mIndex : -1;
            mOps[pos++] = op.enterAnim;
            mOps[pos++] = op.exitAnim;
            mOps[pos++] = op.popEnterAnim;
            mOps[pos++] = op.popExitAnim;
            if (op.removed != null) {
                final int N = op.removed.size();
                mOps[pos++] = N;
                for (int i = 0; i < N; i++) {
                    mOps[pos++] = op.removed.get(i).mIndex;
                }
            } else {
                mOps[pos++] = 0;
            }
            op = op.next;
        }
        mTransition = bse.mTransition;
        mTransitionStyle = bse.mTransitionStyle;
        mName = bse.mName;
        mIndex = bse.mIndex;
        mBreadCrumbTitleRes = bse.mBreadCrumbTitleRes;
        mBreadCrumbTitleText = bse.mBreadCrumbTitleText;
        mBreadCrumbShortTitleRes = bse.mBreadCrumbShortTitleRes;
        mBreadCrumbShortTitleText = bse.mBreadCrumbShortTitleText;
        mSharedElementSourceNames = bse.mSharedElementSourceNames;
        mSharedElementTargetNames = bse.mSharedElementTargetNames;
    }

    public BackStackState(Parcel in) {
        mOps = in.createIntArray();
        mTransition = in.readInt();
        mTransitionStyle = in.readInt();
        mName = in.readString();
        mIndex = in.readInt();
        mBreadCrumbTitleRes = in.readInt();
        mBreadCrumbTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
        mBreadCrumbShortTitleRes = in.readInt();
        mBreadCrumbShortTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
        mSharedElementSourceNames = in.createStringArrayList();
        mSharedElementTargetNames = in.createStringArrayList();
    }
}

/**
 * @hide Entry of an operation on the fragment back stack.
 */
final class BackStackRecord extends FragmentTransaction implements
        FragmentManager.BackStackEntry, Runnable {
    static final String TAG = FragmentManagerImpl.TAG;
    final FragmentManagerImpl mManager;
    static final class Op {
        Op next;
        Op prev;
        int cmd;
        Fragment fragment;
        int enterAnim;
        int exitAnim;
        int popEnterAnim;
        int popExitAnim;
        ArrayList<Fragment> removed;
    }

    Op mHead;
    Op mTail;
    public BackStackRecord(FragmentManagerImpl manager) {
        mManager = manager;
    }
    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++;
    }

    public FragmentTransaction add(Fragment fragment, String tag) {
        doAddOp(0, fragment, tag, OP_ADD);
        return this;
    }

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

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

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

    public FragmentTransaction replace(int containerViewId, Fragment fragment) {
        return replace(containerViewId, fragment, null);
    }

    public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
        if (containerViewId == 0) {
            throw new IllegalArgumentException("Must use non-zero containerViewId");
        }

        doAddOp(containerViewId, fragment, tag, OP_REPLACE);
        return this;
    }

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

        return this;
    }

    public FragmentTransaction hide(Fragment fragment) {
        Op op = new Op();
        op.cmd = OP_HIDE;
        op.fragment = fragment;
        addOp(op);

        return this;
    }

    public FragmentTransaction show(Fragment fragment) {
        Op op = new Op();
        op.cmd = OP_SHOW;
        op.fragment = fragment;
        addOp(op);

        return this;
    }

    public FragmentTransaction detach(Fragment fragment) {
        Op op = new Op();
        op.cmd = OP_DETACH;
        op.fragment = fragment;
        addOp(op);

        return this;
    }

    public FragmentTransaction attach(Fragment fragment) {
        Op op = new Op();
        op.cmd = OP_ATTACH;
        op.fragment = fragment;
        addOp(op);
        return this;
    }
    public int commit() {
        return commitInternal(false);
    }
    private TransitionState beginTransition(SparseArray<Fragment> firstOutFragments,
            SparseArray<Fragment> lastInFragments, boolean isBack) {
        TransitionState state = new TransitionState();

        // Adding a non-existent target view makes sure that the transitions don‘t target
        // any views by default. They‘ll only target the views we tell add. If we don‘t
        // add any, then no views will be targeted.
        state.nonExistentView = new View(mManager.mHost.getContext());

        // Go over all leaving fragments.
        for (int i = 0; i < firstOutFragments.size(); i++) {
            int containerId = firstOutFragments.keyAt(i);
            configureTransitions(containerId, state, isBack, firstOutFragments,
                    lastInFragments);
        }

        // Now go over all entering fragments that didn‘t have a leaving fragment.
        for (int i = 0; i < lastInFragments.size(); i++) {
            int containerId = lastInFragments.keyAt(i);
            if (firstOutFragments.get(containerId) == null) {
                configureTransitions(containerId, state, isBack, firstOutFragments,
                        lastInFragments);
            }
        }
        return state;
    }
    public class TransitionState {
        public ArrayMap<String, String> nameOverrides = new ArrayMap<String, String>();
        public View enteringEpicenterView;
        public View nonExistentView;
    }
}

3、Fragment的add、replace、remove等操作小结

Fragment的addreplace操作这些都是针对栈顶的Fragment,其中每一次addreplace之后,这些操作都会封装成Op对象并保存到BackStackRecord里,也就说这些操作并未真正的起作用,还得把这些操作集合commit(作用类似数据库事务的commit)。在成功commit之后FragmentManager才会将此次所有Op放到主线程中去按顺序执行(主程序去调用BackStackRecord的run方法)。

  • FragmentTransaction的add操作的管理类似一个队列的,在此队列中,根据add进去的先后顺序形成了一个”链表“
  • 在同一个容器内多次执行add操作,显示的总是最后一次add 的Fragment,但其他 Fragment 依然是存在于容器内的
  • remove操作其实相当于把指定的Fragment从“链表”中移除,如果移除的是顶层的Fragment那么显示的自然是次顶层的Fragment;若移除的是中间层的Fragment,那么显示的依然是原来的顶层Fragment。
  • attach和detach:使用attach之后Fragment对象的isAdded()返回的值是true,使用detach之后Fragment对象的isAdded()返回的值是false,
  • hide和show操作其作用就如字面意思一样,但show操作的不是顶层Fragment是无法显示出来的,同样的如果我们hide顶层Fragment则自动显示次顶层。
  • replace操作,原本按照官方的描述是先把容器内的Fragment清除掉,再添加新的Fragment,但是在测试过程中发现并没有清除掉,或许是个bug吧。而且在实际使用replace来切换页面,每次切换的时候,Fragment都需要重新实例化,重新加载一边数据,很明显非常消耗资源和性能的。普遍采取的替代方案之一:切换时hide,add另一个Fragement;当再次返回的时候,在hide当前的,show之前被hide的Fragment。(好处之一就是不用重复实例化Fragment)
private void chooseFragement(Fragment currFragement, Fragment targetFragment) {
    if (!to.isAdded()) { /*先判断是否被add过*/
        transaction.hide(currFragement).add(R.id.id_content_frame, Fragment).commit(); // 隐藏当前的fragment,add下一个到Activity中
    } else {
        transaction.hide(currFragement).show(Fragment).commit(); // 隐藏当前的fragment,显示下一个
    }
}

4、FragmentTransaction事务相关

与数据库事务类似,Fragement事务也是支持类似回滚的功能的。

4.1、调用FragmentTransaction对象的addToBackStack将事务添加到所谓的Back Stack(此栈由Activity管理),当我们按返回键时可以返回到此FragmentTransaction提交之前对应的Fragment状态。

具体用法就是我们在commit之前,先使用addToBackStack将对应的FragmentTransaction对象添加到回退栈

fragmentManager=getFragmentManager();        fragmentManager.beginTransaction().add(viewGroupId,fragment,tag)
                .addToBackStack(tag).commit();

特别地当你remove一个fragment的时候,如果commit()之前没有调用 addToBackStack(),那个fragment将会是destroyed;如果调用了addToBackStack(),这个fragment 会是stopped,可以通过返回键来恢复

4.2、FragmentManager对象的popBackStack将指定的操作弹出Back Stack

popBackStack的默认就是将最上层的操作弹出(模拟用户按下返回键),当栈中有多层时,我们也可以通过id或tag标识来指定弹出到的操作所在层。值得注意的是popBackStack针对的是一次操作集合(或者类似Git里的修改),比如说现在一个容器内add了Fragement1并添加至Back Stack再commit,再接着add Fragment2、Fragment3也添加至Back Stack再commit,最后我们执行popBackStack,那么容器此时的状态就是回退到了刚刚add Fragment1并commit成功之后的状态。(如果add 的顺序是Fragment2–>Fragment3,回退时则是Fragment3–>Fragment2) 

/*
*id: 当提交变更时transaction.commit()的返回值。
*name: transaction.addToBackStack(String tag)中的tag值;
*flags:有两个取值:0或FragmentManager.POP_BACK_STACK_INCLUSIVE;
当取值0时,表示除了参数一指定这一层之上的所有层都退出栈,指定的这一层为栈顶层; 当取值POP_BACK_STACK_INCLUSIVE时,表示连着参数一指定的这一层一起退出栈
*/
abstract void   popBackStack();

abstract void   popBackStack(String name, int flags)

abstract void   popBackStack(int id, int flags)

这个函数是异步的:它将弹出栈的请求加入队列,但是这个动作直到应用回到事件循环才会执行。所以使用popBackStack()来弹出栈内容的话,调用该方法后会将事物操作插入到FragmentManager的操作队列,只有当轮询到该事物时才能执行。如果想立即执行事物的话,需要使用下面几个对应的方法:

popBackStackImmediate()  
popBackStackImmediate(String tag)  
popBackStackImmediate(String tag, int flag)  
popBackStackImmediate(int id, int flag)  

4.3、关于commit

调用commit()方法并不能立即执行transaction中包含的操作,commit()方法只是把transaction加入Activity的Main线程队列中。但是,如果觉得有必要的话,可以调用executePendingTransactions()方法来立即执行commit()提供的transaction。然而这样做通常是没有必要的,除非这个transaction被其他线程依赖。还有你只能在Activity存储它的状态(当用户要离开Activity时)之前调用commit(),如果在存储状态之后调用commit(),将会抛出一个异常。这是因为当Activity再次被恢复时commit之后的状态将丢失。假如丢失也没关系,我们可以使用commitAllowingStateLoss()方法。

4.4、关于Back Stack状态的监听

通过添加监听器,就可以在Back Stack状态改变时,及时监听到并作相应操作。

abstract void addOnBackStackChangedListener(listener);//添加监听器  
abstract void removeOnBackStackChangedListener(listener);//移除监听器  

4.4.1添加监听

在Acitivty里为FragmentManager添加一个监听器,一般是在onCreate方法里

    private class BackStackChangedListener implements FragmentManager.OnBackStackChangedListener{

        @Override
        public void onBackStackChanged() {

        }
    }
BackStackChangedListener listener=new BackStackChangedListener();
FragmentManager fragmentManager = getFragmentManager();  
fragmentManager.addOnBackStackChangedListener(listener );

4.4.2移除监听

在Acitivty里为FragmentManager添加一个监听器,一般是在onDestory方法里

 fragmentManager.removeOnBackStackChangedListener(listener);  

一般来说,无论是何种监听,在Activity或者界面销毁时,都要记得remove掉,否则会潜藏着OOM的隐患。

以上是关于Android进阶——Fragment详解之操作原理的主要内容,如果未能解决你的问题,请参考以下文章

Android之Fragment的前世今生

android之Fragment基础详解

Android入门——Fragment详解之基本概念与用法

Android - fragment之间数据传递

Android 进阶——SystemServer进程启动之Watchdog 详解

Android 进阶——Framework核心 之Binder Java成员类详解