Fragment相关

fragment基本操作:

 getSupportFragmentManager().beginTransaction()
                    .add(id, fragment, tag)
                    .addToBackStack(null)
                    .commit();

以下就v4包下的FragmentManager进行源码分析。

1、FragmentManager#beginTransaction

该方法返回一个BackStackRecord对象
该对象内部有一个成员变量mOps:

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

Op是一个封装了fragment操作以及转场动画的bean:

    static final class Op {
        int cmd;//dvfragment的操作,如add remove replace等
        Fragment fragment;
        int enterAnim;
        int exitAnim;
        int popEnterAnim;
        int popExitAnim;
    }

2、BackStackRecord#add(id, fragment, tag)

该方法会将add操作封装成一个Op对象,然后存入mOps集合中。

3、 BackStackRecord#addToBackStack(null)

@Override
    public FragmentTransaction addToBackStack(String name) {
        if (!mAllowAddToBackStack) {
            throw new IllegalStateException(
                    "This FragmentTransaction is not allowed to be added to the back stack.");
        }
        mAddToBackStack = true;
        mName = name;
        return this;
    }

注意mAddToBackStack 这个变量,很关键!该方法最重要的是让这个变量设为true。它的作用下面会讲到。

4、BackStackRecord#commit()

该方法最终调用commitInternal:

int commitInternal(boolean allowStateLoss) {
        if (mCommitted) throw new IllegalStateException("commit already called");
        mCommitted = true;
        if (addToBackStack) {
            //关键1
            mIndex = mManager.allocBackStackIndex(this);
        } else {
            mIndex = -1;
        }
        //关键2
        mManager.enqueueAction(this, allowStateLoss);
        return mIndex;
    }

如果调用了步骤3的addToBackStack方法,addToBackStack此时为true,进入判断执行关键1处代码,它的源码为:

ArrayList<BackStackRecord> mBackStackIndices;
ArrayList<Integer> mAvailBackStackIndices;
public int allocBackStackIndex(BackStackRecord bse) {
        synchronized (this) {
            if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) {
                if (mBackStackIndices == null) {
                    mBackStackIndices = new ArrayList<BackStackRecord>();
                }
                int index = mBackStackIndices.size();
                mBackStackIndices.add(bse);
                return index;

            } else {
                //移除最后一个元素,返回最后一个元素的值。
                int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1);
                mBackStackIndices.set(index, bse);
                return index;
            }
        }
    }

刚开始这段代码看的我是云里雾里,没办法,只能写个demo来debug运行不断分析了:
demo中不停的开启新fragment,且加入回退栈,此时mAvailBackStackIndices=null,走第一个判断,所有事务(BackStackRecord)都会添加到mBackStackIndices 集合中,并不会进else判断。
然后多次按返回键回退几个fragment,紧接着重新开启fragment,此时才会进入else判断,意味着mAvailBackStackIndices这个集合此时不再为空且有内容了,更巧的是它的集合长度等于mAvailBackStackIndices的长度,那么mAvailBackStackIndices在哪里初始化并存放内容的呢?搜索mAvailBackStackIndices = new,找到下面这段代码,并debug验证了每次按返回键移除fragment都会执行这段代码:

    public void freeBackStackIndex(int index) {
        synchronized (this) {
            mBackStackIndices.set(index, null);
            if (mAvailBackStackIndices == null) {
                //在这里初始化了
                mAvailBackStackIndices = new ArrayList<Integer>();
            }
            if (DEBUG) Log.v(TAG, "Freeing back stack index " + index);
            //最后添加到mAvailBackStackIndices 后返回的index,会陆续添加到mAvailBackStackIndices中。
            mAvailBackStackIndices.add(index);
        }
    }

总结下mBackStackIndicesmAvailBackStackIndices的操作:
前者会将BackStackRecord对象存入,这很正常;后者作用是:在从mBackStackIndices集合中移除BackStackRecord对象的时候,将该被移除的BackStackRecord对象所在原集合中的index存入mAvailBackStackIndices,注意加粗的移除,方式并非直接remove,而是ArrayList.set(index,null)。
为什么不直接用mBackStackIndices,添加就add,移除就remove呢?
我猜想可能是mBackStackIndices是ArrayList,ArryList基于数组实现,这样避免了释放数组空间并再次开辟的耗时,达到空间换时间提高效率的目的。
至于mBackStackIndices有啥用?好像跟什么adb命令,打印输出Activity信息啥的有关......走错码头了,看关键2吧。

5、FragmentManager#enqueueAction(OpGenerator action, boolean allowStateLoss)

关键2处代码调用mManager.enqueueAction(this, allowStateLoss),参数this即BackStackRecord对象。

public void enqueueAction(OpGenerator 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<>();
            }
            //将action添加到mPendingActions集合中
            mPendingActions.add(action);
            //执行action任务
            scheduleCommit();
        }
    }

scheduleCommit()方法很简单,它利用handler,将任务发送到主线程的任务队列,执行execPendingActions():

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

while循环的判断条件执行generateOpsForPendingActions:

private boolean generateOpsForPendingActions(ArrayList<BackStackRecord> records,
            ArrayList<Boolean> isPop) {
        boolean didSomething = false;
        synchronized (this) {
            //当集合为空或没内容时,返回false,防止while循环无限轮询
            if (mPendingActions == null || mPendingActions.size() == 0) {
                return false;
            }

            final int numActions = mPendingActions.size();
            for (int i = 0; i < numActions; i++) {
                //这里是关键,且generateOps返回值关系到上面的while循环
                didSomething |= mPendingActions.get(i).generateOps(records, isPop);
            }
            //清空集合
            mPendingActions.clear();
            mHost.getHandler().removeCallbacks(mExecCommit);
        }
        return didSomething;
    }

generateOps方法点进入,发现是OpGenerator接口的一个抽象方法,它的实现其中一个是在BackStackRecord中,那就看一下,
BackStackRecord#generateOps:

    @Override
    public boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop) {
        records.add(this);
        isRecordPop.add(false);
        if (mAddToBackStack) {
            //关键
            mManager.addBackStackState(this);
        }
        //始终返回true,上面的while条件一定成立
        return true;
    }

步骤3中,mAddToBackStack被设为true,进入判断,执行 mManager.addBackStackState(this);回到FragmentManager中来:

   void addBackStackState(BackStackRecord state) {
        if (mBackStack == null) {
            mBackStack = new ArrayList<BackStackRecord>();
        }
        mBackStack.add(state);
    }

至此,我们可以说,凡是调用了.addToBackStack(null)方法,都会将相关的BackStackRecord放入FragmentManager中的成员变量mBackStack集合中。

generateOps方法这里始终返回true,那么就会执行while内的任务removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);,看名字是移除多余的操作并执行,然后经过一系列调用,最终执行的是record.executePopOps(moveToState);或者是record.executeOps()方法。先看record.executeOps():

6、BackStackRecord#executeOps

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;
            if (f != null) {
                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;
                case OP_SET_PRIMARY_NAV:
                    mManager.setPrimaryNavigationFragment(f);
                    break;
                case OP_UNSET_PRIMARY_NAV:
                    mManager.setPrimaryNavigationFragment(null);
                    break;
                default:
                    throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
            }
            if (!mReorderingAllowed && op.cmd != OP_ADD && f != null) {
                mManager.moveFragmentToExpectedState(f);
            }
        }
        if (!mReorderingAllowed) {
            // Added fragments are added at the end to comply with prior behavior.
            mManager.moveToState(mManager.mCurState, true);
        }
    }

遍历BackStackRecord对象中的mOps 集合,swith到mOps对象封装的任务,最终调用FragmentManager的相关方法执行该任务。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容