Fragment运行机制源码分析(二)

尊重原创:http://blog.csdn.net/yuanzeyao/article/details/52895029

如果你喜欢我的文章,可以关注左边微信公众号,我会定期推送文章跟新。
在前面的文章中,我们详细分析了Fragment的生命周期,以及讲解了Activity如何控制Fragment的生命周期,按照计划,本篇文章就来分析一下add,replace,remove,hide,show等api到底做了生命逻辑,如果大家还没有阅读Fragment运行机制分析(一),那么建议先阅读一下。

在具体分析源码之前,我们来看看平时我们是如何使用这些api的,已add为例:

        FragmentManager mFragmentManager = getSupportFragmentManager();
        mFragmentManager.beginTransaction()
                .add(R.id.fl_container,HomeFragment.newInstance(), HomeFragment.class.getSimpleName())
                .commit();

我们发现,向Activity中添加一个Fragment非常简单,就4步:

  1. 获取Activity中的FragmentManager,通过上篇文章分析,我们知道返回的是FragmentManagerImpl对象.
  2. 调用beginTransaction() 返回一个FragmentTransaction,它仅仅是一个抽象类,后面我们会分析一下它的子类.
  3. 调用add方法,第一个参数的Fragment将要加入的容器id,第二个参数是要添加的Fragment,第三个参数是此Fragment的tag,设置了这个tag之后,后面就可以通过方法findFragmentByTag 方法找到此Fragment,主要用于Fragment的自动恢复.
  4. 执行commit方法.

    同理replace,remove,hide,show 等api的调用方式都是一模一样(当然有时也可能会调用addToBackStack).

接下来,我们从第2步开始研究每一步做了什么,进入FragmentManagerImpl,查看beginTransaction 方法逻辑:

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

看到这里大家就知道FragmentTransaction 的实现类就是BackStackRecord,我们接下来看看此类的继承关系:

final class BackStackRecord extends FragmentTransaction implements
        FragmentManager.BackStackEntry, Runnable

你会发现BackStackRecord 不仅继承了FragmentTransaction 类,而且还实现了Runnable 接口,说明BackStackRecord 应该是一个线程.

在第2步拿到了FragmentTransaction 之后就调用add 方法,我们进入BackStackRecordadd方法:(Note:add 方法有多个重载,但是里面的逻辑基本一致,所以我只分析其中一个)

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

原来add 方法仅仅调用了一个doAddOp 方法,我们通过这个名字可以猜想一下,这个此方法的功能:添加一个Add 操作。add 操作返回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);
    }

我们接下来仔细分析这个方法:

  1. FragmentManagerImpl 对象保存到Fragment.mFragmentManager 中.
  2. 检查Fragment 是否设置过tag,如果设置过,并且和参数中的tag不一致,那么抛出异常。如果没有设置过,那么保存tag.
  3. 检查Fragment,如果Fragment 中的containerId已经不等于0,并且和参数的containerId不一致,那么抛出异常,如果等于0,那么保存containerId
  4. 创建Op 对象,在此对象保存cmd参数和Fragment参数,这里cmd 的值为OP_ADD.
  5. 调用addOp 方法,并将上面创建的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 对象加入链表的尾部。ok,add 方法分析完毕,原来add方法仅仅是创建了一个”添加操作”,然后放入链表尾部,接下来,我们分析最后一步:commit方法:

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

太简单,不用解释,直接进入commitInternal 方法

int commitInternal(boolean allowStateLoss) {
        if (mCommitted) throw new IllegalStateException("commit already called");
        if (FragmentManagerImpl.DEBUG) {
            Log.v(TAG, "Commit: " + this);
            LogWriter logw = new LogWriter(TAG);
            PrintWriter pw = new PrintWriter(logw);
            dump("  ", null, pw, null);
        }
        mCommitted = true;
        if (mAddToBackStack) {
            mIndex = mManager.allocBackStackIndex(this);
        } else {
            mIndex = -1;
        }
        mManager.enqueueAction(this, allowStateLoss);
        return mIndex;
    }

此方法的逻辑也是非常清晰:首先检查commit 方法是否已经执行过,如果多次执行此commit 方法就会抛出异常。判断是否调用过addBackToStack (我们这里没有调用),如果调用了那么需要由FragmentManager 分配一个index,否则返回-1,最后调用mManager.enqueueAction(this, allowStateLoss); 方法.

这里我们看到如果在调试阶段,我们想看FragmentManager的相关Log,我们可以调用FragmentManager的enableDebugLogging方法将Log打开,并通过FragmentManager过滤.

接下来进入FragmentManagerImpl.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);
            }
        }
    }

这个方法也很简单,首先检查是否允许状态丢失,这里我们传入的false,所以首先调用checkStateLoss,此方法我就不分析,其实就是检查onSaveInstanceState 方法是否已经执行,如果执行了,那么会抛出异常。检查通过后将Runnable 加入一个待执行列表mPendingActions(根据前面的分析我们知道这个Runnable 就是BackStackRecord 对象)。紧接着将mExecCommit 放入Handler 中,其实mExecCommit 也是一个Runnable ,它的run 方法如下:

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

run 方法就是调用了execPendingActions 方法,逻辑如下:

/**
     * Only call from main thread!
     */
    public boolean execPendingActions() {
        if (mExecutingActions) {
            throw new IllegalStateException("Recursive entry to executePendingTransactions");
        }

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

        boolean didSomething = false;

        while (true) {
            int numActions;

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

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

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

        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 对吧,所以我们需要进入BackStackRecord 的run方法研究一下,run方法比较长,里面有一个switch语句:

case OP_ADD: {
                    Fragment f = op.fragment;
                    f.mNextAnim = enterAnim;
                    mManager.addFragment(f, false);
                } break;

我贴出来的case就是对应的OP_ADD操作的api,通过前面分析可以知道,当我们执行了add 操作之后,最终调用的是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);
            }
        }
    }

addFragment 的主要工作如下:

  1. 将当前fragment 放入mActivie列表并分配一个index,这个功能是通过调用makeActive 实现

    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);
    }
  2. fragment 放入mAdd 列表,由于addFragment 的第二个参数传入的是false,所以此时不会执行moveToState 方法,(moveToState 在前面一篇文章已经详细分析了),那么此方法什么时候执行的呢?大家回到BackStackRecord 的run方法,最后面就会调用此方法

    mManager.moveToState(mManager.mCurState, transition, transitionStyle, true);
    
        if (mAddToBackStack) {
            mManager.addBackStackState(this);
        }

    OK,到此为止详细add 操作大家已经非常清楚了,使用同样的分析方法,我们来看看remove 的逻辑,你会发现remove 最终也是调用到了BackStackRecordrun 方法,只不过对应的是下面的case:

case OP_REMOVE: {
                    Fragment f = op.fragment;
                    f.mNextAnim = exitAnim;
                    mManager.removeFragment(f, transition, transitionStyle);

调用的是FragmentManager.removeFragment 方法:

 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 (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);
        }
    }
  1. removeFragment 首先检查此fragment 是否调用过addBackToStack,如果没有调用则inactive 为true
  2. 检查fragment.mDetached 或者inactive 是否为true,如果为true,那么将fragmentmAdded 列表中删除,并调用moveToState方法

    这里可能有些同学会有疑问:moveToState 会不会执行两次:BackStackRecord.runremoveFragmnet 方法都会调用,但是如果你仔细分析,你会发现当removeFragment 执行之后,Fragment 会从mActive 移除,所以第二次执行moveToState的时候 发现Fragment 为null,所以什么都不干。

至于其他的api(replacehide ,show) 你们可以自己分析,其实逻辑是一样的,这里我就不分析了。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值