Fragment之工作流程

本文深入解析Fragment的基本使用,包括添加、替换、移除等操作,并详细分析了commit()和commitAllowingStateLoss()的区别,以及Fragment状态同步的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简单使用

建议在看这边博文前,先看下这篇:Fragment之设计思路
先来看看Fragment的简单使用,然后再跟下去:

		SimpleFragment fragment = new SimpleFragment();
		FragmentManager manager = getSupportFragmentManager();
        FragmentTransaction transaction = manager.beginTransaction();
        transaction.add(parentViewId,fragment,"simple_fragment").commit();

这段代码很常见,但是这里面做了什么事情,可能就不知道了,那就来看看了。先来看下getSupportFragmentManager(),它实际返回的是FragmentManagerImpl对象,在FragmentHostCallback中实例化的,这在Fragment之设计思路中有讲到,接下来调用的是beginTransaction(),那就到FragmentManager中去看看它做了些什么操作:

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

很简单,就是创建了一个BackStackRecord对象,这里就简称为事物对象了,其内部就是记录即将添加的Fragment,使用的是双向链表的方式,这里先来看下记录Fragment双向链表的结构:

    static final int OP_NULL = 0;
    static final int OP_ADD = 1;
    static final int OP_REPLACE = 2;
    static final int OP_REMOVE = 3;
    static final int OP_HIDE = 4;
    static final int OP_SHOW = 5;
    static final int OP_DETACH = 6;
    static final int OP_ATTACH = 7;
    static final class Op {
        Op next;
        Op prev;
        int cmd;
        Fragment fragment;
        int enterAnim;
        int exitAnim;
        int popEnterAnim;
        int popExitAnim;
        ArrayList<Fragment> removed;
    }

这个链表结构还是很好理解的,prev就是在这个之前添加的,next就是在这个之后添加的,Fragment就是当前添加的Fragment,cmd可以理解为一个标识,他标记的是当前Fragment是通过add()、remove(),replace()、hide()、show()、attch()、detach()这些方法中哪一个方法添加进来的,这些方法中有些是需要传view的id的,这些id所代表的view将会作为fragment生成view的父view,这里先大概说下这些方法的作用:

add()

会将fragment添加进FragmentManager进行管理,如果是带有viewid的那个方法,那么fragment生成的view默认是显示的;

remove()

会将之前添加进来的fragment从FragmentManager中移除;

replace()

这个需要传一个viewId,就是说需要将之前添加到这个viewId下fragment全部替换掉,内部实现替换可分为两步,一是先将这个viewId下的fragment移除,相当于调用了remove(),再将这个viewId下的所有fragment移除后,然后再将替换的fragment添加进来,相当于调用了add(),可以说这个方法是add()和remove()的组合;

hide()

调用这个方法的前提是已经有对应的fragment添加进来了,要知道每个fragment内部都可以维护一个view,这个view默认是显示的,当然要有对应的父view才能看到,当调用hide()时,其内部实际调用的 setVisibility(View.GONE)

show()

上面说了hide(),这里要理解show就简单多了, 其内部实际调用的就是setVisibility(View.GONE)

detach()

会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。

attach()

重建view视图,附加到UI上并显示。
方法的大概意思就说到这,接下来就去源码中看看,毕竟最好的理解就是看源码,接着回到上面的流程,现在要说的就是这个add()方法:

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

下来看看add()中几个参数的意思:
1、containerViewId:这是一个view的id,后面fragment生成的view将会作为子view添加到它的下面;
2、fragment:这个好理解,就是需要添加的fragment;
3、tag:当前fragment的一个标识,fragment添加到FragmentManager中后,当需要从FragmentManager中再次拿到这个fragment时,就可以用到这个tag
在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);
    }

还是很好理解的,先是对Fragment中的几个成员变量(mFragmentManager ,mTag ,mContainerId,mFragmentId )进行了赋值,赋值完后就是创建了一个链表结构的对象,Op类的结构上面已经提到,最后在调用的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对象形成一个链表结构,以及赋值一些fragment中view显示的一些动画。add()方法的整个过程就分析完了,对add()理解了,其他方法也就好理解,基本都是对链表结构进行的操作,感兴趣的可以自己看下,很好理解的,代码如下:

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

说完add()后,接下来就是最后一步commit()了:

	public int commit() {
        return commitInternal(false);
    }

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

一个事物对象只能commit()一次,否则就会报 “commit already called” 异常,接着就是对mAddToBackStack的一个判断,这个值默认是false的,当调用了addToBackStack()后会赋值为true,它的作用就是当我们点击返回键时会返回到上一个fragment,而不会退出当前activity,这个可以看下FragmentActivity的onBackPressed():

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

可以看到这里有个判断,如果当前Activity中有fragment添加到了回退栈中,那么就会显示添加到回退栈中的fragment,如果没有就会当前activity,好了,接下来要执行的就是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);
            }
        }
    }

传进来两个参数,一是Runnable,BackStackRecord本身实现了这个接口,第二个allowStateLoss,是一个boolean值,这个值取决于提交事务时使用的方法:

    public int commit() {
        return commitInternal(false);
    }

    public int commitAllowingStateLoss() {
        return commitInternal(true);
    }

就是取决于这两方法,那调用这两个方法的区别在哪里?回到上面的enqueueAction(),可以看到allowStateLoss为false时会调用到一个方法,来看看里面做了什么:

    private void checkStateLoss() {
        if (mStateSaved) {
            throw new IllegalStateException(
                    "Can not perform this action after onSaveInstanceState");
        }
        if (mNoTransactionsBecause != null) {
            throw new IllegalStateException(
                    "Can not perform this action inside of " + mNoTransactionsBecause);
        }
    }

就是抛了两个异常,这里的mStateSaved默认是为false的,那什么时候为true,比如在横竖屏切换时,activity是会销毁的,fragment也是会销毁然后重建,重建的时候,这时mStateSaved就会赋值为true,进入这里时就会抛异常了,到这,就可以顺带把commit()和commitAllowingStateLoss()区别总结下了:commit()方法提交的事物会检查fragment创建的状态,比如横竖屏切换后,检查状态时就会报异常,而commitAllowingStateLoss()提交的事物就不会去检查状态,这也就是这个两个方法最最本质的区别。
开了点小差,再次回到主线enqueueAction()方法中来,这里最终会去执行mExecCommit对象的run():

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

看来实现逻辑在execPendingActions()中:

    public boolean execPendingActions() {
        ...

        boolean didSomething = false;

        while (true) {
            int numActions;
            
            synchronized (this) {
                if (mPendingActions == null || mPendingActions.size() == 0) {
                    break;
                }
                //enqueueAction()中实例化的集合对象,不记得的可以再去看下enqueueAction
                //下面的这些操作主要作用是mPendingActions中保存的对象复制到mTmpActions数组中去
                numActions = mPendingActions.size();
                if (mTmpActions == null || mTmpActions.length < numActions) {
                    mTmpActions = new Runnable[numActions];
                }
                mPendingActions.toArray(mTmpActions);
                mPendingActions.clear();
                mHost.getHandler().removeCallbacks(mExecCommit);
            }
            
            mExecutingActions = true;
            //拿到复制的数据后,接下来就是开始执行里面的run()方法了,前面有提到BackStackRecord实现了Runnable接口,下面的循环就是去执行实现的run方法
            for (int i=0; i<numActions; i++) {
                mTmpActions[i].run();
                mTmpActions[i] = null;
            }
            mExecutingActions = false;
            didSomething = true;
        }
        //首次进来默认为false,如果进入到这里,那么就会将fragment的状态执行的与activity一致,这里就不跟下去了
        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;
    }

对于这个方法的作用代码中有注释,这里就不多解释了,接下来就是去看看BackStackRecord中run()方法的实现了:

public void run() {
        ... 
        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);
        }
    }

前面添加进来的fragment目前还是以链表的形式保存着,这里所作的操作就是对这个链表进行遍历,然后根据不同的状态做不同的操作,这里以add()为例:

		Fragment f = op.fragment;
        f.mNextAnim = enterAnim;
        mManager.addFragment(f, false);

很简单,就是调用了FragmentManager的addFragment():

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

整体的逻辑还是很简单,先是将fragment添加到mAdded集合,接着就是对fragment初始化一些属性,moveToStateNow传过来是false,所以下面那个方法也就不会执行了,这里在去看下makeActive(fragment):

    void makeActive(Fragment f) {
        if (f.mIndex >= 0) {
            return;
        }
        
        if (mAvailIndices == null || mAvailIndices.size() <= 0) {
            if (mActive == null) {
                mActive = new ArrayList<Fragment>();
            }
            f.setIndex(mActive.size(), mParent);
            mActive.add(f);
            
        } else {
            f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1), mParent);
            mActive.set(f.mIndex, f);
        }
        if (DEBUG) Log.v(TAG, "Allocated fragment index " + f);
    }

目的就是将fragment添加到mActive集合中,后面同步fragment状态时,用到的就是这个集合,addFragment()就分析完了,总结下,就是将fragment添加到mAdd和mActive集合中以及初始化一些fragment中的成员变量。
到这还没结束,还有最重要的一点没说,继续回到BackStackRecord的run()方法中,在最后有一个方法:

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

这个方法就是同步fragment状态,里面会同步所有fragment的状态,来看下:

void moveToState(int newState, int transit, int transitStyle, boolean always) {
        ...
        if (mActive != null) {      
            for (int i=0; i<mActive.size(); i++) {
                Fragment f = mActive.get(i);
                if (f != null) {
                    moveToState(f, newState, transit, transitStyle, false);
                    ...
                }
            }
            ...
        }
    }

简化了下代码,逻辑还是很清晰的,遍历所有添加进来的fragment,然后执行moveToState(f, newState, transit, transitStyle, false):

void moveToState(Fragment f, int newState, int transit, int transitionStyle,
            boolean keepActive) {
        ...
        if (f.mState < newState) { 
            switch (f.mState) {
                case Fragment.INITIALIZING:
                    ...
                    f.onAttach(mHost.getContext());
                    ...
                    if (f.mParentFragment == null) {
                        mHost.onAttachFragment(f);
                    }

                    if (!f.mRetaining) {
                        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) {
                            ...
                            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) {
                           ...
                            f.mContainer = container;
                            f.mView = f.performCreateView(f.getLayoutInflater(
                                    f.mSavedFragmentState), container, f.mSavedFragmentState);
                            if (f.mView != null) {
                                ...
                                if (container != null) {
                                    ...
                                    container.addView(f.mView);
                                }
                                if (f.mHidden) f.mView.setVisibility(View.GONE);
                                f.onViewCreated(f.mView, f.mSavedFragmentState);
                            } else {
                                f.mInnerView = null;
                            }
                        }

                        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);
                        f.performStart();
                    }
                case Fragment.STARTED:
                    if (newState > Fragment.STARTED) {
                        if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
                        f.mResumed = true;
                        f.performResume();
                        f.mSavedFragmentState = null;
                        f.mSavedViewState = null;
                    }
            }
        } else if (f.mState > newState) {
            ...
        }
        
        f.mState = newState;
    }

这里简化了很多代码,当activity的生命周期改变时,都会调用到这里,这里就是去同步fragment的生命周期,fragment的生命周期方法在这里都可以找到调用,这里在来看点上面的伪代码:

		f.mContainer = container;
        f.mView = f.performCreateView(f.getLayoutInflater(f.mSavedFragmentState), container, f.mSavedFragmentState);
        container.addView(f.mView);

这里container是activity给到fragment的父布局,在创建view后,就将这个view添加到父布局中,这个moveToState()方法可以好好品味下,对理解fragment很有帮助。好了,一下子发现扯了好多了,就到这里了。欢迎留言一起学习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值