深入理解Android碎片栈及detach方法(和remove的区别)

找了一圈,发现网上讲碎片的文章都是...,所以还是自己找源码吧


事务的实现类找了一下,发现是BackStackRecord,它里面存放的是你每一次事务操作的所有元数据(碎片,操作方式(add,remove...))。他放在了Op类里。用ArrayList类来保存。但是不要以为它就是你碎片的栈了。碎片栈存放在FragmentManagerImpl类里。每当BackStackRecord的Op集合改变以后,FragmentManagerImpl就会监听到这个情况,从而对真正的碎片栈进行操作。我们这里想探寻的是detach方法,所以只讲这部分的源码,其他东西可以自己看,很好找,代码也很好理解。


当你transaction.detach就是执行这里的代码了(代码在BackStackRecord类里)

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

    return this;
}

仅仅是元数据集合,记录了每一次操作。这里的detach仅仅记录了操作,但是并没有真正地去执行操作。


所以你得看看执行操作的地方

void executeOps() {
    final int numOps = mOps.size();
    for (int opNum = 0; opNum < numOps; opNum++) {
        final Op op = mOps.get(opNum);
        final Fragment f = op.fragment;
        f.setNextTransition(mTransition, mTransitionStyle);
        switch (op.cmd) {
            case OP_ADD:
                f.setNextAnim(op.enterAnim);
                mManager.addFragment(f, false);
                break;
            case OP_REMOVE:
                f.setNextAnim(op.exitAnim);
                mManager.removeFragment(f);
                break;
            case OP_HIDE:
                f.setNextAnim(op.exitAnim);
                mManager.hideFragment(f);
                break;
            case OP_SHOW:
                f.setNextAnim(op.enterAnim);
                mManager.showFragment(f);
                break;
            case OP_DETACH:
                f.setNextAnim(op.exitAnim);
                mManager.detachFragment(f);
                break;
            case OP_ATTACH:
                f.setNextAnim(op.enterAnim);
                mManager.attachFragment(f);
                break;

这里调用的就是FragmentManagerImpl类里的方法,所以我们得知了,其实FragmentManagerImpl类里才是真正存放了碎片栈,和对碎片栈进行的操作方法。


我们看一下detach方法。

public void detachFragment(Fragment fragment) {
    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;
        }
    }
}

你只需要关注一句,remove。mAdd就是被添加到栈(也就是ViewGroup如FrameLayout中)的碎片的集合。有人可能会问,这和remove有啥区别?


看一下remove

public void removeFragment(Fragment fragment) {
    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;
    }
}

也是remove,多了一句mRemoving = true。有个锤子用?


所以可以考虑看一下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.mView == null) {
            fragment.mHiddenChanged = false;
        }
        if (fragment.mHasMenu && fragment.mMenuVisible) {
            mNeedMenuInvalidate = true;
        }
        if (moveToStateNow) {
            moveToState(fragment);
        }
    }
}
根本看不出所以然嘛!但是你想想之前的executeOps方法。你应该领悟到了,FragmentManagerImpl类顾名思义就是碎片的管理者(的实现类)啊!


不过别急着去看BackStackRecord的add方法,先看看attach方法

@Override
public FragmentTransaction attach(Fragment fragment) {
    Op op = new Op();
    op.cmd = OP_ATTACH;
    op.fragment = fragment;
    addOp(op);

    return this;
}
看看,依然只是状态的改变,所以得知了,detach是把碎片从mAdd中移除了,其实remove也是从mAdd中移除了,可是不同的地方在BackStackRecord里。当你执行add的时候,你会传入新的碎片,传入要插入的ViewGroup,然后进行Op类状态的重新绑定。而detach,attach并不需要这样,他们换过来换过去也只是更改了几个变量而已。

而detach掉的碎片虽然从mAdd集合中移除了,但是他的状态都没有变。


看看add方法,验证猜测

@Override
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) {
    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;

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

    Op op = new Op();
    op.cmd = opcmd;
    op.fragment = fragment;
    addOp(op);
}

得出结论:remove和detach的都是从mAdd中移除,(可以这么认为)但是remove掉的碎片失去了Op类的信息,detach掉的碎片,依然保有。所以detach可以被认为是一个轻量级的移除(通俗点就是detach仅仅是和activity解绑,remove不仅解绑了,还从活动中移除了,甚至执行到了onDestory)


最后一个疑问:为什么要有这个BackStackRecord呢?

其实,你所commit的事务并不是立刻提交的!而是经过九曲十八弯,最终抵达消息队列,再在子线程里解决这一个个操作。详细可以看文章:commit和commitNow的区别:http://blog.csdn.net/qq_36523667/article/details/78656078


(这两个类的源码值得一看,又简单,又实用,回报很高。此外看源码可以提升自己的逻辑,判空逻辑就是一点)

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值