FragmentManager 源码解析

使用Fragment的时候,我们要使用两个类:FragmentManger和FragmentTransaction。我们通过FragmentManger提供的方法可以获取到FragmentTransaction对象。

第一步:FragmentManger 是一个抽象类

在Activity中,我们通过使用getSupportFragmentManger()方法获取该对象的实例。接下来我们使用beginTranscation()方法获取一个FragmentTransaction对象。但是因为FragmentManger是一个抽象类,没有方法的实现,他的内部提供了一个实现的子类为FragmentManagerImpl。然后看下实现的beginTranscation()方法如下:

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

创建一个BackStackRecord()对象。那这个对象是什么鬼,然后发现原来FramentTransaction也是个抽象的类,其子类是BackStackRecord类。也就是说通过beginTranscation()方法我们就获取到了FragmentTransaction实现的子类。

第二步:添加Fragment

获取到FragmentTransaction实例后,我们就使用了add()方法进行添加Fragment对象。如下:

getSupportFragmentManager()
        .beginTransaction()
        .add(R.id.fragment_content, mCurrentFragment)
        .commitAllowingStateLoss();

看下add()内部的代码:

public abstract FragmentTransaction add(@IdRes int containerViewId, Fragment fragment);

然后进入到子类中看实现的代码:

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

然后进入到doAddOp()方法中看下:

private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
    final Class fragmentClass = fragment.getClass();
    final int modifiers = fragmentClass.getModifiers();
    if (fragmentClass.isAnonymousClass() || !Modifier.isPublic(modifiers)
            || (fragmentClass.isMemberClass() && !Modifier.isStatic(modifiers))) {
        throw new IllegalStateException("Fragment " + fragmentClass.getCanonicalName()
                + " must be a public static class to be  properly recreated from"
                + " instance state.");
    }

    fragment.mFragmentManager = mManager; //设置的framngent对应的FragmentManger

    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 (containerViewId == View.NO_ID) { // Used to mark a View that has no ID.
            throw new IllegalArgumentException("Can't add fragment "
                    + fragment + " with tag " + tag + " to container view with no id");
        }
        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;
    }

    addOp(new Op(opcmd, fragment));
}

其中最后一个参数opcmd代表的是标志位,在内部还是几个如下:

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 int OP_SET_PRIMARY_NAV = 8;
static final int OP_UNSET_PRIMARY_NAV = 9;

然后进入到addop()方法中看下:

void addOp(Op op) {
    mOps.add(op);
    op.enterAnim = mEnterAnim;
    op.exitAnim = mExitAnim;
    op.popEnterAnim = mPopEnterAnim;
    op.popExitAnim = mPopExitAnim;
}

其中mOps是一个列表,这个方法就将保存了Fragment的op对象放入到列表中:

ArrayList<Op> mOps = new ArrayList<>();

然后看下Op这个对象:

static final class Op {
    int cmd;
    Fragment fragment;
    int enterAnim;
    int exitAnim;
    int popEnterAnim;
    int popExitAnim;

    Op() {
    }

    Op(int cmd, Fragment fragment) {
        this.cmd = cmd;
        this.fragment = fragment;
    }
}

第三步:commit()提交任务

依然进入到FragmentTranscation类查看该方法如下:

public abstract int commit(); //提交一个事务

然后进入到真正实现的BackStackRecord类中查看该方法的实现,如下:

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

注意这个方法他传入了一个false,这个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);
        pw.close();
    }
    mCommitted = true;
    if (mAddToBackStack) {
        mIndex = mManager.allocBackStackIndex(this);
    } else {
        mIndex = -1;
    }
    mManager.enqueueAction(this, allowStateLoss);
    return mIndex;
}

这个方法将mCommitted设置为true,也就是说已经提交了。mAddToBackStack是判断是否可以添加,在初始化的时候设置为true.

进入到allocBackStatckIndex()方法看下:

public int allocBackStackIndex(BackStackRecord bse) {
    synchronized (this) {
        if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) {
            if (mBackStackIndices == null) {
                mBackStackIndices = new ArrayList<BackStackRecord>();
            }
            int index = mBackStackIndices.size();
            if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
            mBackStackIndices.add(bse);
            return index;

        } else {
            int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1);
            if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
            mBackStackIndices.set(index, bse);
            return index;
        }
    }
}

这个方法中,我们将FragmentTranscation的子类BackStackRecord添加到了一个存放的队列中。

然后现在我们传递过来的allowStateLoss = false,然后我们进入到enqueueAction()方法中看看做了些什么?

/**
 * Adds an action to the queue of pending actions.
 *
 * @param action the action to add
 * @param allowStateLoss whether to allow loss of state information
 * @throws IllegalStateException if the activity has been destroyed
 */
public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
    if (!allowStateLoss) {
        checkStateLoss();
    }
    synchronized (this) {
        if (mDestroyed || mHost == null) {
            if (allowStateLoss) {
                // This FragmentManager isn't attached, so drop the entire transaction.
                return;
            }
            throw new IllegalStateException("Activity has been destroyed");
        }
        if (mPendingActions == null) {
            mPendingActions = new ArrayList<>();
        }
        mPendingActions.add(action);
        scheduleCommit();
    }
}

这个方法的目的就是添加活动到列表中,然后进行一次检测。我们之前传allowStateLoss为false,首先会进入到checkStateLoss()方法中,进入看看这个方法都做了些什么?

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

只是进行了一个isStateSaved的判断处理,如果isStateSaved()返回的是true的话,就报异常。我们看看这个方法里面,如下:

@Override
public boolean isStateSaved() {
    // See saveAllState() for the explanation of this.  We do this for
    // all platform versions, to keep our behavior more consistent between
    // them.
    return mStateSaved || mStopped;
}

非常的简单,就是判断mStateSaved和mStopped有一个为真就返回true。然后,我们看看mStateSaved是在哪里进行设置的?几个方法中,我发现基本都是将mStateSaved设置成false的。那什么地方设置成了true呢,这样才会报crash。然后在这个方法中,我们发现了设置:

Parcelable saveAllState() {
    // Make sure all pending operations have now been executed to get
    // our state update-to-date.
    mStateSaved = true;
    mSavedNonConfig = null;
    if (mActive == null || mActive.size() <= 0) {
        return null;
    }

哪是哪里调用的这个方法呢?继续寻找,然后发现在Actvitiy的onSaveInstanceState()方法中调用了这个方法,如下:

/**
 * Save all appropriate fragment state.
 */
@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    markState(getSupportFragmentManager(), Lifecycle.State.CREATED);
    Parcelable p = mFragments.saveAllState();
    if (p != null) {
        outState.putParcelable(FRAGMENTS_TAG, p);
    }
}
也就是说,当activity调用了onSaveInstance()方法后,我们在此进行checkStateLoss()的方法时,就会报异常。

然后在回到 enqueueAction()方法中,现在看下scheduleCommit()方法,做了些什么工作,如下:

private void scheduleCommit() {
    synchronized (this) {
        boolean postponeReady =
                mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
        boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
        if (postponeReady || pendingReady) {
            mHost.getHandler().removeCallbacks(mExecCommit);
            mHost.getHandler().post(mExecCommit);
        }
    }
}

这个方法是获取了activity的handler,然后切换到主线程中执行了mExecCommit任务,我们看看做了些什么?

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

进行进入到方法中看下:

/**
 * Only call from main thread!
 */
public boolean execPendingActions() {
    ensureExecReady(true);
    boolean didSomething = false;
    while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
        mExecutingActions = true;
        try {
            removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
        } finally {
            cleanupExec();
        }
        didSomething = true;
    }
    doPendingDeferredStart();
    burpActive();
    return didSomething;
}

大概的意思也就是应该通知activity刷新ui。

第四步:使用show展示

首先进入到FragmentTransaction看下这个方法,如下:

/**
 * Shows a previously hidden fragment.  This is only relevant for fragments whose
 * views have been added to a container, as this will cause the view to
 * be shown.
 * 
 * @param fragment The fragment to be shown.
 * 
 * @return Returns the same FragmentTransaction instance.
 */
public abstract FragmentTransaction show(Fragment fragment);

然后进入到BackStackRecord看下子类的实现,如下:

@Override
public FragmentTransaction show(Fragment fragment) {
    addOp(new Op(OP_SHOW, fragment));
    return this;
}

非常简单的通过addOp()。然后在使用commit()进行提交,走相同的步骤。

同时如果我们不希望进行checkStateLoss()的检测工作。我们可以使用commitAllowingStateLoss()方法,看看他和commit的区别

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

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

就是进入到commitInternal()方法中传入的值是不同的,这个参数就是决定是否要走checkStateLoss()这个方法,是否在执行完onSaveInstatncState()方法后,再执行其他show()等方法的时候,是否要报异常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值