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

来源:互联网 发布:linux ddos攻击脚本 编辑:程序博客网 时间:2024/06/05 02:48

尊重原创: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) 你们可以自己分析,其实逻辑是一样的,这里我就不分析了。

1 0
原创粉丝点击