[Android]Fragment源码分析(肆) Fragment栈管理

Fragment的栈是Fragment管理颇为出彩的一部分,它跟Activity栈的本质差异除了在数据结构上和逻辑上的不同之外,主要区别还在于:

1.Fragment管理是在进程空间内的

2.Fragment的管理一般情况下是一个Window下进行的。

Fragment的管理在一个进程空间内是比较好理解的,因为我们知道Activity的管理其实相对复杂,它的管理是通过IPC调用,IPC的一端是我们的Client,而作为Server的是Ams服务。Activity的管理是基于Window的,而Fragment的管理普遍是基于同一个window下的View来实现的。在我看来,Fragment管理无疑是Android的福音,因为它更轻量级,相对更快。而且这种Fragment注册也可以不通过注册AndroidManifest.xml的方式来实现,意味着你可以实现一个非常好的插件系统。

或许各位看官还不理解,为何子墨兄为何要在开篇如此浓墨重彩,那是因为子墨希望大家尽量的将代码结构往Fragment管理上靠。当然我还是习惯性的提醒各位,Fragment不是View,不是控件,不要用View的观点去看待它,它就是一个容器,比Activity轻量级的容器。

我们回到本章的课题,Fragment的栈管理,或许你还不能很直观的了解什么是Fragment栈,我们引入一段代码:

            FragmentTransaction ft = this.getSupportFragmentManager().beginTransaction();
            fragment = new TestFragment1();
            ft.add(R.id.fragmentContainer, fragment, "test");
            ft.setTransition( FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
            <span style="color:#ff0000;"><strong>ft.addToBackStack("test");</strong></span>
            ft.commitAllowingStateLoss();

上一章我们浓墨重彩写了Fragment的事务管理,我们知道,我们提交的这个事务将会在下一个UI线程消息中执行,我在里面用红色标注了一段代码addToBackStack的方法。实际上,对于里面的name参数我们可有可无。它只是我们在dump的时候的一个标识符号。我们现在这个方法上打个断点,我们知道,当我们调用这个方法的直接结果就是在我们按Back的时候,它会返回到我们上一个事务中去。我们知道对于Back按钮的处理是在Activity的onBackPressed回调中。

<pre name="code" class="java">android.support.v4.app.FragmentManager:
@Override
    public boolean popBackStackImmediate() {
        checkStateLoss();
        executePendingTransactions();
        return popBackStackState(mActivity.mHandler, null, -1, 0);
    }

FragmentActivity:/** * Take care of popping the fragment back stack or finishing the activity * as appropriate. */ public void onBackPressed() { if (!mFragments.popBackStackImmediate()) { finish(); } }


FragmentManager在PopStack的时候会调用一遍executePendingTransactions,我们上一章说过,基于事务的Fragment模型会将事务存在在队列中,而这个方法就是将队列中的所有事务执行一遍。Fragment的事务管理是采用备忘录的方式,所以你所有的操作都会记录在它自己的数据结构中,而且每一个数据操作都是可逆的。这是Fragment的两点之一,也就是当你进行add的操作时候,必然有一个remove操作与其对应,这种对应的操作被记录在BackStackRecord的popFromBackStack方法中。我们先来看下这部分逻辑:

android.support.v4.app.BackStackRecord:
public void popFromBackStack(boolean doStateMove) {
 ...
switch (op.cmd) {
            <span style="color:#ff0000;">case OP_ADD</span>: {
                Fragment f = op.fragment;
                f.mNextAnim = op.popExitAnim;
                <span style="color:#cc0000;">mManager.removeFragment</span>(f,
                        FragmentManagerImpl.reverseTransit(mTransition),
                        mTransitionStyle);
            }
}

我看看到,实际上在popFromBackStack中,BackStackRecord对本身的记录进行了逆操作,这就是为什么在你在回退Fragment栈的时候它能用逆的方式来进行Fragment管理。我们回头再说FragmentManager。为了实现Fragment的回退,首先我们要记录整个Fragment的调用流程,还有回调Fragment对应的BackStackRecord的pop方法。Fragment调用的入口之一在boolean popBackStackState(Handler handler, String
name, int id, int flags)中:

boolean popBackStackState(Handler handler, String name, int id, int flags) {
        if (mBackStack == null) {
            return false;
        }
        if (name == null && id < 0 && (flags & POP_BACK_STACK_INCLUSIVE) == 0) {
           <span style="color:#3366ff;"> int last = mBackStack.size() - 1;
            if (last < 0) {
                return false;
            }
            final BackStackRecord bss = mBackStack.remove(last);
            bss.popFromBackStack(true);
            reportBackStackChanged();</span>
        } else {
            <span style="color:#ff0000;">int index = -1;
            if (name != null || id >= 0) {
                // If a name or ID is specified, look for that place in
                // the stack.
                index = mBackStack.size() - 1;
                while (index >= 0) {
                    BackStackRecord bss = mBackStack.get(index);
                    if (name != null && name.equals(bss.getName())) {
                        break;
                    }
                    if (id >= 0 && id == bss.mIndex) {
                        break;
                    }
                    index--;
                }
                if (index < 0) {
                    return false;
                }
                if ((flags & POP_BACK_STACK_INCLUSIVE) != 0) {
                    index--;
                    // Consume all following entries that match.
                    while (index >= 0) {
                        BackStackRecord bss = mBackStack.get(index);
                        if ((name != null && name.equals(bss.getName()))
                                || (id >= 0 && id == bss.mIndex)) {
                            index--;
                            continue;
                        }
                        break;
                    }
                }
            }
            if (index == mBackStack.size() - 1) {
                return false;
            }
            final ArrayList<BackStackRecord> states = new ArrayList<BackStackRecord>();
            for (int i = mBackStack.size() - 1; i > index; i--) {
                states.add(mBackStack.remove(i));
            }
            final int LAST = states.size() - 1;
            for (int i = 0; i <= LAST; i++) {
                states.get(i).popFromBackStack(i == LAST);
            }
            reportBackStackChanged();</span>
        }
        return true;
    }

我们可以看出,实际上Fragment管理Fragment存储的数据结构是:mBackStack对象。它的类型是强类型的ArrayList。我们不难猜出它是采用线性表的方式来模拟Stack数据结构。蓝色部分代码比较好了解,直接取得最后一个状态,然后通过回调它的pop方法来结束Fragment对自己的管理。有些人可能会带有困惑,Fragment已经在FragmentManager中存在有记录,为何要多创建一个BackStackRecord对象来记录呢?实际上这个问题跟Activity的管理很相似,我能给你的最直观的回答就是侧重点不同,FragmentManager的侧重点是为了管理Fragment的状态,而BackStackRecord的目的是为了记录Fragment的操作。为了方便大家了解红色部分的逻辑我先引入一段代码:

if (v == view1) {
            FragmentTransaction ft = this.getSupportFragmentManager().beginTransaction();
            fragment = new TestFragment1();
            ft.add(R.id.fragmentContainer, fragment, "test");
            ft.setTransition( FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
            <strong><span style="color:#ff0000;">ft.addToBackStack("test"+index);</span></strong>
            ft.commitAllowingStateLoss();
            index ++;
        } else {
            this.getSupportFragmentManager().popBackStack("test2", FragmentManager.POP_BACK_STACK_INCLUSIVE);
        }

当你add到BackStack里面10个的Fragment的时候,pop到test2位置的fragment的时候,它会将BackStack中test2之后的记录都clear掉,对,就是Activity的clearTop或者Activity的启动参数设置。当然,Activity的Intent的Flag和启动模式本身就是一种东西,只不过做了包装而已。我们通过现象在回到代码就非常的好理解,它无非就是取得对应的BackStackRecord,然后记录在一个List里面,然后进行批量的消除。

好了,文章写到这里,相信你对Fragment的Stack的管理有了一个基本的认识,但是我们还是没有涉及Fragment如何加入Stack的问题。我们回调BackStackRecord的addToStack方法:

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

这里,BackStackRecord对mAddToBackStack被设置为true.在Commit的时候会分配一个index号码:

int commitInternal(boolean allowStateLoss) {
        if (mCommitted)
            throw new IllegalStateException("commit already called");
        mCommitted = true;
        if (mAddToBackStack) {
            mIndex = mManager.<strong><span style="color:#33cc00;">allocBackStackIndex</span></strong>(this);
        } else {
            mIndex = -1;
        }
        mManager.enqueueAction(this, allowStateLoss);
        return mIndex;
    }

实际上,对于Manager分配Index的方式非常简单:

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

            } else {
                int index = mAvailBackStackIndices
                        .remove(mAvailBackStackIndices.size() - 1);
                mBackStackIndices.set(index, bse);
                return index;
            }
        }
    }

这里主要是两个变量mAvailBackStackIndices和mBackStackIndices。实际上我们可以比较简单的理解这两个变量,当我们pop出Fragment的时候,它会将它的index存放在mAvailBackStackIndices队列中,当我们需要申请一个index的时候如果mAvailBackStackIndices中存在,那么就返回暂存在这个对象中的索引值。

[Android]Fragment源码分析(肆) Fragment栈管理

时间: 2024-08-11 05:44:45

[Android]Fragment源码分析(肆) Fragment栈管理的相关文章

[Android]Volley源码分析(肆)应用

通过前面的讲述,相信你已经对Volley的原理有了一定了解.本章将举一些我们能在应用中直接用到的例子,第一个例子是 NetworkImageView类,其实NetworkImageView顾名思义就是将异步的操作封装在了控件本身,这种设计可以充分保留控件的移植性和维护性.NetworkImageView通过调用setImageUrl来指定具体的url: public void setImageUrl(String url, ImageLoader imageLoader) { mUrl = ur

[Android]Fragment源码分析(一) 构造

Fragment是Android3.0之后提供的api,被大家广泛所熟知的主要原因还是因为随即附带的ViewPager控件.虽然我并不喜欢用它,但是它确实是一个相对不错的控件.还是我的一贯作风,我将从源码上向大家展示什么是Fragment.我们先写一个简单的代码对Fragment有个直观的认识:(为了保证我们方便调试,我们可以直接使用V4提供的源码包) FragmentTransaction t = getSupportFragmentManager().beginTransaction();

[Android]Fragment源码分析(三) 事务

Fragment管理中,不得不谈到的就是它的事务管理,它的事务管理写的非常的出彩.我们先引入一个简单常用的Fragment事务管理代码片段: FragmentTransaction ft = this.getSupportFragmentManager().beginTransaction(); ft.add(R.id.fragmentContainer, fragment, "tag"); ft.addToBackStack("<span style="fo

[Android]Fragment源码分析(二) 状态

我们上一讲,抛出来一个问题,就是当Activity的onCreateView的时候,是如何构造Fragment中的View参数.要回答这个问题我们先要了解Fragment的状态,这是Fragment管理中非常重要的一环.我们先来看一下FragmentActivity提供的一些核心回调: @Override protected void onCreate(Bundle savedInstanceState) { mFragments.attachActivity(this, mContainer,

Android debuggerd 源码分析

debuggerd 简介 Android系统自带一个实用的程序异常退出的诊断daemon debuggerd.此进程可以侦测到程序崩溃,并将崩溃时的进程状态信息输出到文件和串口中,以供开发人员分析调试使用.Debuggerd的数据被保存在/data/tombstone/目录下,共可保存10个文件,当超过10个时,会覆盖重写最早生产的文件.串口中,则直接用DEBUG的tag,输出logcat信息. Linux kernel有自己的一套signal机制,在应用程序崩溃时,通常系统内核都会发送sign

Android数据库源码分析(2)-SQLiteDatabase的实现以及多线程行为

Android数据库源码分析(2)-SQLiteDatabase的实现以及多线程行为 本系列主要关注安卓数据库的线程行为,分为四个部分: (1)SQLiteOpenHelper的getReadableDatabase和getWritableDatabase (2)SQLiteDatabase的实现以及多线程行为 (3)连接缓存池SQLiteConnectionPool (4)SQLiteDatabase多线程实践 本篇主要关注SQLiteDatabase的线程同步实现与架构实现. 1 SQLit

[Android]Volley源码分析(四)

上篇中有提到NetworkDispatcher是通过mNetwork(Network类型)来进行网络访问的,现在来看一下关于Network是如何进行网络访问的. Network部分的类图: Network有一个实现类BasicNetwork,它有一个mHttpStack的属性,实际的网络请求是由这个mHttpStack来进行的,看BasicNetwork的performRequest()方法, 1 @Override 2 public NetworkResponse performRequest

android 从源码分析为什么Listview初次显示时没滚动却自动调用onScroll方法的原因

我们做Listview的分批加载时,需要为Listview调用setOnScrollListener(具体代码可见我上一篇博客) 可是,我们会发现,当运行程序时,listview明明没有滚动,那为什么系统会调用onScroll方法呢?(补充:此时onScrollStateChanged并不会调用) 我们先看setOnScrollListener源码: public void setOnScrollListener(OnScrollListener l) { mOnScrollListener =

[Android]Volley源码分析(二)Cache

Cache作为Volley最为核心的一部分,Volley花了重彩来实现它.本章我们顺着Volley的源码思路往下,来看下Volley对Cache的处理逻辑. 我们回想一下昨天的简单代码,我们的入口是从构造一个Request队列开始的,而我们并不直接调用new来构造,而是将控制权反转给Volley这个静态工厂来构造. com.android.volley.toolbox.Volley: public static RequestQueue newRequestQueue(Context conte