FragmentManager实际上是用链表来管理Fragment的

姓名 连嘉玮 学号 16040120089

转自:http://www.jianshu.com/p/2412fca60cfe?utm_campaign=hugo&utm_medium=reader_share&utm_content=note&utm_source=qq

有删节

【嵌牛导读】:FragmentManager

【嵌牛鼻子】:一个误解

【嵌牛提问】:FragmentManager到底是用来干什么的?

【嵌牛正文】:

之前一直有一个误解,认为FragmentManager是用栈来管理Fragment的,直到今天深扒了Framework源码后,才发现一直搞错了。可能你也跟我有一样的误解,希望这篇文章能让你树立正确的观点。

一、我是怎么开始怀疑自己原来的观点的

今天在复习Fragment相关知识的时候,突然想到一个有意思的话题:假设在Activity的界面上有一个FrameLayout,它的id是container1,那么,能不能在这个container1中添加多个Fragment呢?

于是果断建立一个Demo项目进行实验(文末有源码地址),在container1中添加了一个Fragment1,然后又添加了一个Fragment2,发现没有报错!当然,此时还不能确定两个Fragment都添加到了container1中了。

紧接着,我调用FragmentManager的findFragmentById(R.id.container1)方法,测试发现,返回的是Fragment2。

从结果来看,貌似Fragment1没有在container1中,于是我又做了一个实验,那就是在添加Fragment1时为它添加了一个Tag,即"fragment1",然后,我再次调用FragmentManager的findFragmentByTag("fragment1"),果然,查找到了Fragment1,这说明Fragment1和Fragment2都添加成功了。

那么问题来了!!

很多文章和书上都说,FragmentManager是靠container的id来区分Fragment的,现在Fragment1和Fragment2是同一个container id 。FragmentManager是怎么管理它们的呢?

到这一步,我还是觉得用栈可以解释通,后添加的Fragment在栈顶,之前添加的在下面,只让栈顶的Fragment显示出来,在调用findFragmentById时也只返回栈顶的Fragment。

于是,我又做了一个实验,上面的container1不是一个FrameLayout吗,我把它改成vertical的LinearLayout,再次运行Demo项目,WORD 天,Fragment1和Fragment2的界面都显示出来了,如下所示:

two_fragment_in_one_container.PNG

到这一步,我已经很怀疑FragmentManager会用栈来管理Fragment了。

然后,我进一步想,一个Activity的界面上可以有许多个FrameLayout,它们可以作为container2,container3......,每一个container中都可以添加许多的Fragment,如果FragmentManager使用一个栈来管理这么多的Fragment,遇到remove一个非栈顶的Fragment时,岂不费劲死!

至此,我已经不相信自己之前的观点了,所谓一言不和,就扒源码,我开始了自己的探索路程。

二、先找到FragmentManager

因为在Activitty中是通过getSupportFragmentM这个方法获取FragmentManager的实例的,我毫不留情地在这个方法上点击了。这种感觉就像潜水,现在的深度是5米,感觉棒棒哒!

眼前的景象是:我进入了FragmentActivity内部,并且看到了这个方法:

/**

    * Return the FragmentManager for interacting with fragments associated

    * with this activity.

    */

    public FragmentManager getSupportFragmentManager() {

        return mFragments.getSupportFragmentManager();

    }

我二话不说,继续点,再点,再点,终于潜到了一个完全没有光的深度,好黑啊,我打开了头顶上的探照灯,发现自己到了FragmentManager的一个内部类:FragmentManagerImpl

哦,终于见到了FragmentManager的真身啦!

三、再找到 FragmentTransaction

稍加停留后,我就继续往下潜了,我找到FragmentManagerImpl的beginTransaction方法,勇敢地点击了进去,经过几次点击,终于找到了FragmentTransaction的真身,原来是一个叫做BackStackRecord的类。

我找到它的add()方法,继续往下点,来到了一个私有方法处,该方法的核心代码如下:

private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd){

          ...

        fragment.mTag = tag;

          ...

        fragment.mContainerId = fragment.mFragmentId = containerViewId;

          ...

        Op op = new Op();

        op.cmd = opcmd;

        op.fragment = fragment;

        addOp(op);

}

看到这几行代码,我已经觉得不虚此潜了!但对真理的崇高追求让我勇敢地在addOp(op)上点了进去......

四、发现真相!!

进到addOp方法内部,我看到这样几行闪闪发光的代码:

//这里的op就是Fragment的载体

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

    }

看到mHead、mTail字样,终于确定了,FragmentManager是用链表来管理Fragment的。

**

不是用栈

不是用栈

不是用栈

**

五、进一步探索findFragmentById方法

文章开头的例子中,我们看到findFragmentById返回了Fragment2,于是我很好奇,这个方法是怎么实现的,潜一次水不容易,我决定一并弄清它的真相。

这个方法的代码是这样的:

@Override

    public Fragment findFragmentById(int id) {

/*mAdded是一个ArrayList<Fragment>,里面存的是该Activity界面上所有

的container中最新添加的Fragment*/

        if (mAdded != null) {

            // First look through added fragments.

            for (int i=mAdded.size()-1; i>=0; i--) {

                Fragment f = mAdded.get(i);

                if (f != null && f.mFragmentId == id) {

                    return f;

                }

            }

        }

/* mActive也是一个ArrayList<Fragment>,里面保存该Activity上所有添加

过的Fragment,在我们上文提到的Fragment1和Fragment2同时显示在一个

LinearLayout的例子中,两个Fragment都在mActive中,但是只有Fragment2

在mAdded,这也就解释了为什么findFragmentById会返回Fragment2啦,

因为是先从mAdded查找的,而且是倒着查的*/

        if (mActive != null) {

            // Now for any known fragment.

            for (int i=mActive.size()-1; i>=0; i--) {

                Fragment f = mActive.get(i);

                if (f != null && f.mFragmentId == id) {

                    return f;

                }

            }

        }

        return null;

    }

至此,我的潜水过程完全结束了。有句话讲:太阳底下没有新鲜事,现在我觉得源码面前没有秘密。

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

推荐阅读更多精彩内容