Fragment的BackStack管理过程

  • 1. Fragment基本用法
    为了管理Activity中的fragments,需要调用Activity中的getFragmentManager()方法。因为FragmentManager的API是在Android 3.0,也即API level 11开始引入的,所以对于之前的版本,需要使用support library v4中的FragmentActivity,并且使用getSupportFragmentManager()方法。
    用FragmentManager可以做的工作有:
    得到Activity中存在的fragment:
      使用findFragmentById()或findFragmentByTag()方法。
      将fragment弹出back stack:
    popBackStack():
        将back stack中最后一次的fragment转换弹出。如果没有可以出栈的东西,返回false。
     这个函数是异步的:它将弹出栈的请求加入队列,但是这个动作直到应用回到事件循环才会执行。
    为back stack加上监听器:
      addOnBackStackChangedListener()
    使用Fragment时,可以执行一些动作,比如增加、移除、替换等。所有这些改变构成一个集合,这个集合被叫做一个transaction。
    可以调用FragmentTransaction中的方法来处理这个transaction.
    以这样得到FragmentTransaction类的实例: 

    [java] view plaincopy

    1. FragmentManager fragmentManager = getFragmentManager();
    2. FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

    每个transaction是一组同时执行的变化的集合。用add(), remove(), replace()方法,把所有需要的变化加进去,然后调用commit()方法,将这些变化应用。在commit()方法之前,你可以调用addToBackStack(),把这个transaction加入back stack中去,这个back stack是由activity管理的,当用户按返回键时,就会回到上一个fragment的状态。下面的代码非常典型,用一个新的fragment取代之前的fragment,并且将之前的状态存储在back stack中。

    [java] view plaincopy

    1. // Create new fragment and transaction
    2. Fragment newFragment = new ExampleFragment();
    3. FragmentTransaction transaction = getFragmentManager().beginTransaction();
    4. // Replace whatever is in the fragment_container view with this fragment,
    5. // and add the transaction to the back stack
    6. transaction.replace(R.id.fragment_container, newFragment);
    7. transaction.addToBackStack(null);
    8. // Commit the transaction
    9. transaction.commit();

    通过调用addToBackStack(),commit()的一系列转换作为一个transaction被存储在back stack中,用户按Back键可以返回上一个转换前的状态。
    调用commit()方法并不能立即执行transaction中包含的改变动作,commit()方法把transaction加入activity的UI线程队列中。

    下面我们对上述代码中出现的函数进行分析,以此来逐步学习Fragment的管理机制。

    getSupportFragmentManager():

    [java] view plaincopy

    1. public FragmentManager getSupportFragmentManager() {
    2. return mFragments;
    3. }

    该函数返回类型是FragmentManager,FragmentManager是一个抽象类,其实现类是FragmentManager.FragmentManagerImpl

    beginTransaction():
    该函数在FragmentManagerIMpl中的源码如下:

    [java] view plaincopy

    1. public FragmentTransaction beginTransaction() {
    2. return new BackStackRecord(this);
    3. }

    返回一个BackStackRecord对象,该对象是FragmentTranscation的一个子类。
    BackStackRecord的声明如下:

    [java] view plaincopy

    1. final class BackStackRecord extends FragmentTransaction implements
    2. FragmentManager.BackStackEntry, Runnable {...}

    该类实现了一个重要的接口:FragmentManager.BackStackEntry, 该接口代表了fragment back stack的一个入口。可以用FragmentManager.getBackStackEntry()来检索BackStackEntry。    
    接下来执行transaction.replace(), 查看BackStackRecord,调用过程源码如下:

    [java] view plaincopy

    1. public FragmentTransaction replace(int containerViewId, Fragment fragment) {
    2. return replace(containerViewId, fragment, null);
    3. }
    4. public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
    5. if (containerViewId == 0) {
    6. throw new IllegalArgumentException("Must use non-zero containerViewId");
    7. }
    8. doAddOp(containerViewId, fragment, tag, OP_REPLACE);
    9. return this;
    10. }

    我们发现,replace()最终调用的函数为doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd), 将Fragment和对Fragment所进行的操作放到op链表中:

    [java] view plaincopy

    1. private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
    2. fragment.mFragmentManager = mManager;
    3. if (tag != null) {
    4. if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
    5. throw new IllegalStateException("Can‘t change tag of fragment "
    6. + fragment + ": was " + fragment.mTag
    7. + " now " + tag);
    8. }
    9. fragment.mTag = tag;
    10. }
    11. if (containerViewId != 0) {
    12. if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
    13. throw new IllegalStateException("Can‘t change container ID of fragment "
    14. + fragment + ": was " + fragment.mFragmentId
    15. + " now " + containerViewId);
    16. }
    17. fragment.mContainerId = fragment.mFragmentId = containerViewId;
    18. }
    19. Op op = new Op();
    20. op.cmd = opcmd;
    21. op.fragment = fragment;
    22. addOp(op);
    23. }

    该函数首先设置fragment的mFragmentManager属性,然后再设置其mContainerId和mFragmentId,最后创建Op对象,然设置相应自段,其中cmd自动用来标识事务的类型,分为如下几类:
        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;
    每个字段的意思可直接通过英文名称获知。Op()类是BackStackRecord中声明的结构体,本质上是一个双向链表的Node。addOp()如下:

    [java] view plaincopy

    1. void addOp(Op op) {
    2. if (mHead == null) {
    3. mHead = mTail = op;
    4. } else {
    5. op.prev = mTail;
    6. mTail.next = op;
    7. mTail = op;
    8. }
    9. op.enterAnim = mEnterAnim;
    10. op.exitAnim = mExitAnim;
    11. op.popEnterAnim = mPopEnterAnim;
    12. op.popExitAnim = mPopExitAnim;
    13. mNumOp++;
    14. }

    该函数将Op对象添加到链表的末尾,并将mNumOp的值增一。

    transaction.addToBackStack(null)设置了mAddToBackStack为true,源码如下:

    [java] view plaincopy

    1. public FragmentTransaction addToBackStack(String name) {
    2. if (!mAllowAddToBackStack) {
    3. throw new IllegalStateException(
    4. "This FragmentTransaction is not allowed to be added to the back stack.");
    5. }
    6. mAddToBackStack = true;
    7. mName = name;
    8. return this;
    9. }

    此函数将mAddToBackStack自段设置为true,并设置mName字段。
    最后调用transaction.commit()来执行transaction。commit()的调用过程代码如下:

    [java] view plaincopy

    1. public int commit() {
    2. return commitInternal(false);
    3. }
    4. int commitInternal(boolean allowStateLoss) {
    5. if (mCommitted) throw new IllegalStateException("commit already called");
    6. if (FragmentManagerImpl.DEBUG) {
    7. Log.v(TAG, "Commit: " + this);
    8. LogWriter logw = new LogWriter(TAG);
    9. PrintWriter pw = new PrintWriter(logw);
    10. dump("  ", null, pw, null);
    11. }
    12. mCommitted = true;
    13. if (mAddToBackStack) {
    14. mIndex = mManager.allocBackStackIndex(this);
    15. } else {
    16. mIndex = -1;
    17. }
    18. mManager.enqueueAction(this, allowStateLoss);
    19. return mIndex;
    20. }

    由于mAddToBackStack为true,所以会用FragmentManager为BackstackRecorder也即FragmentTransaction分配一个index,分配过程如下:

    [java] view plaincopy

    1. public int allocBackStackIndex(BackStackRecord bse) {
    2. synchronized (this) {
    3. if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) {
    4. if (mBackStackIndices == null) {
    5. mBackStackIndices = new ArrayList<BackStackRecord>();
    6. }
    7. int index = mBackStackIndices.size();
    8. if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
    9. mBackStackIndices.add(bse);
    10. return index;
    11. } else {
    12. int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1);
    13. if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
    14. mBackStackIndices.set(index, bse);
    15. return index;
    16. }
    17. }
    18. }

    FragmentManager用mAvailBackStackIndices和mBackStackIndices两个数组来为BackStackRecord分配Index。mAvailBackStackIndices用来存储在mBackStackIndices中能够分配的Index,mBackStackIndices则用来保存BackStackRecord。这利用两个数组可以减少对mBackStackIndices的动态分配大小的次数,是一个以空间换时间的策略。上面的代码首先判断是否有可用的Index分配给BackStackRecord,若无则直接将BackStackRecord插入到mBackStackIndices;若存在的话则从mAvailBackStackIndices的队尾取出一个index,然后设置mBackStackIndices中该index下的值。

    让我们回到commit()中,该函数最后执行mManager.enqueAction(),源码如下:

    [html] view plaincopy

    1. public void enqueueAction(Runnable action, boolean allowStateLoss) {
    2. if (!allowStateLoss) {
    3. checkStateLoss();
    4. }
    5. synchronized (this) {
    6. if (mDestroyed || mActivity == null) {
    7. throw new IllegalStateException("Activity has been destroyed");
    8. }
    9. if (mPendingActions == null) {
    10. mPendingActions = new ArrayList<Runnable>();
    11. }
    12. mPendingActions.add(action);
    13. if (mPendingActions.size() == 1) {
    14. mActivity.mHandler.removeCallbacks(mExecCommit);
    15. mActivity.mHandler.post(mExecCommit);
    16. }
    17. }
    18. }

    [java] view plaincopy

    1. 该函数首先进行状态监测,查看该Fagment所在的Activity的生命周期是否处于Saving Activity之前,因为Activity保存状态往往是由用户离开那个Activity所造成的,在此之后执行commit会丢失一些状态信息。针对这种情况,可以使用commitAllowingStateLoss().最后将BackStackRecord加入到执行队列中。当第一次往执行
    2. 队列中添加消息时,首先会从消息队列中所有callback属性为mExecCommit的消息删除,然后重新将mExecCommit添加到消息队列。mExecCommit的定义如下:

    [java] view plaincopy

    1. Runnable mExecCommit = new Runnable() {
    2. @Override
    3. public void run() {
    4. execPendingActions();
    5. }
    6. };

    execPendingActions()只能在主线程内被调用,其内部通过一个循环对mPendingActions中的Actions进行执行。值得注意的是,每执行一次循环,mPendingActions中的所有Action都会被添加到一个临时数组中,然后这个数组被变量一遍以执行数组中的每个Runnable。同时,每个Runnable直接被调用了run,而不是开个线程执行的。当这个Runnable在执行的时候,mPendingActions数组可能会被添加内容。当某一时刻mPendingActions中的内容为空,则while循环退出。此部分代码如下:

    [java] view plaincopy

    1. public boolean execPendingActions() {
    2. if (mExecutingActions) {
    3. throw new IllegalStateException("Recursive entry to executePendingTransactions");
    4. }
    5. if (Looper.myLooper() != mActivity.mHandler.getLooper()) {
    6. throw new IllegalStateException("Must be called from main thread of process");
    7. }
    8. boolean didSomething = false;
    9. while (true) {
    10. int numActions;
    11. synchronized (this) {
    12. if (mPendingActions == null || mPendingActions.size() == 0) {
    13. break;
    14. }
    15. numActions = mPendingActions.size();
    16. if (mTmpActions == null || mTmpActions.length < numActions) {
    17. mTmpActions = new Runnable[numActions];
    18. }
    19. mPendingActions.toArray(mTmpActions);
    20. mPendingActions.clear();
    21. mActivity.mHandler.removeCallbacks(mExecCommit);
    22. }
    23. //一次性执行完数组中所有的Action
    24. mExecutingActions = true;
    25. for (int i=0; i<numActions; i++) {
    26. mTmpActions[i].run();
    27. mTmpActions[i] = null;
    28. }
    29. mExecutingActions = false;
    30. didSomething = true;
    31. }
    32. if (mHavePendingDeferredStart) {
    33. boolean loadersRunning = false;
    34. for (int i=0; i<mActive.size(); i++) {
    35. Fragment f = mActive.get(i);
    36. if (f != null && f.mLoaderManager != null) {
    37. loadersRunning |= f.mLoaderManager.hasRunningLoaders();
    38. }
    39. }
    40. if (!loadersRunning) {
    41. mHavePendingDeferredStart = false;
    42. startPendingDeferredFragments();
    43. }
    44. }
    45. return didSomething;
    46. }

    由于BackstackRecorder实现了Runnable,我们来看看BackStackRecorder中的run(),如下所示:

    [java] view plaincopy

    1. public void run() {
    2. if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Run: " + this);
    3. if (mAddToBackStack) {
    4. if (mIndex < 0) {
    5. throw new IllegalStateException("addToBackStack() called after commit()");
    6. }
    7. }
    8. bumpBackStackNesting(1);
    9. Op op = mHead;
    10. //遍历op,根据cmd的类型对Fragment和FragmentManager进行相应的设置
    11. while (op != null) {
    12. switch (op.cmd) {
    13. case OP_ADD: {
    14. Fragment f = op.fragment;
    15. f.mNextAnim = op.enterAnim;
    16. //将Fragment添加到FragmentManager中,其源码显示是将Fragment添加到FragmentManager中的mActive数组中,并将Fragment添加到了数组mAdded中。
    17. mManager.addFragment(f, false);
    18. } break;
    19. case OP_REPLACE: {
    20. Fragment f = op.fragment;
    21. if (mManager.mAdded != null) {
    22. //遍历已经添加的Fragment,
    23. for (int i=0; i<mManager.mAdded.size(); i++) {
    24. Fragment old = mManager.mAdded.get(i);
    25. if (FragmentManagerImpl.DEBUG) Log.v(TAG,
    26. "OP_REPLACE: adding=" + f + " old=" + old);
    27. //如果发现两个mContainerId一样,则进行特殊处理
    28. if (f == null || old.mContainerId == f.mContainerId) {
    29. if (old == f) {
    30. //两个Fragment一样,则置空,保留old中的Fragment
    31. op.fragment = f = null;
    32. } else {
    33. // 将old fragment加入到 op.removed数组中,保留op中的Fragment
    34. if (op.removed == null) {
    35. op.removed = new ArrayList<Fragment>();
    36. }
    37. op.removed.add(old);
    38. old.mNextAnim = op.exitAnim;
    39. if (mAddToBackStack) {
    40. //设置old Fragment在BackStack中的Number
    41. old.mBackStackNesting += 1;
    42. if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
    43. + old + " to " + old.mBackStackNesting);
    44. }
    45. //对old Fragment设置相应的状态属性,如mAdded、mRemoving, 从FragmentManager中移除oldFrgment的相关属性
    46. mManager.removeFragment(old, mTransition, mTransitionStyle);
    47. }
    48. }
    49. }
    50. }
    51. //将Fragment添加到FragmentManager中
    52. if (f != null) {
    53. f.mNextAnim = op.enterAnim;
    54. mManager.addFragment(f, false);
    55. }
    56. } break;
    57. case OP_REMOVE:
    58. ......
    59. }
    60. op = op.next;
    61. }
    62. //设置Fragment的当前状态,然后根据当前状态来回调Fragment的生命周期中的相关函数。此函数控制了Fragment的生命周期和Fragment的绘制,想要彻底理解Fragment的生命周期的同学可以认真研究此函数。
    63. mManager.moveToState(mManager.mCurState, mTransition,
    64. mTransitionStyle, true);
    65. //将BackStackRecord加入到BackStack中,并回调onBackStackChanged
    66. if (mAddToBackStack) {
    67. mManager.addBackStackState(this);
    68. }
    69. }

    addBackStackState()的源码如下:

    [java] view plaincopy

    1. void addBackStackState(BackStackRecord state) {
    2. if (mBackStack == null) {
    3. mBackStack = new ArrayList<BackStackRecord>();
    4. }
    5. mBackStack.add(state);
    6. //回调onBackStackChanged
    7. reportBackStackChanged();
    8. }

    可以看到传说中的BackStack就是在这里被创建的, FragmentManager中的BackStack主要是用来存储FragmentTransaction的。

    小结:

    FragmentTransaction中的Op链用来保存add、remove、replace等action,在FragmentTransaction的run执行时,Op链会被变量以调整每个节点的内容。

    FragmentManager使用一个BackStack来管理FragmentTransaction;使用mAdded数组来添加被add的Fragment,Fragment的创建、显示等行为都受FragmentManager的控制。
    FragmentManager中的moveToState()是一个非常重要的函数,在FragmentTransaction run的时候被调用。下次我们将深入这个函数。

来自为知笔记(Wiz)

时间: 2024-10-11 00:42:36

Fragment的BackStack管理过程的相关文章

源码分析Fragmentd的BackStack管理过程

1. Fragment基本用法 为了管理Activity中的fragments,需要调用Activity中的getFragmentManager()方法.因为FragmentManager的API是在Android 3.0,也即API level 11开始引入的,所以对于之前的版本,需要使用support library v4中的FragmentActivity,并且使用getSupportFragmentManager()方法. 用FragmentManager可以做的工作有: 得到Activ

Android中保存和恢复Fragment状态的最好方法

英文原文:Probably be the best way (?) to save/restore Android Fragment's state so far 关键点:Fragment的Arguments. 经过这几年使用Fragment之后,我想说,Fragment的确是一种充满智慧的设计,但是使用Fragment时有太多需要我们逐一解决的问题,尤其是在处理数据保持的时候. 首先,虽然其有类似于activity的onSaveInstanceState,但是别想仅仅靠onSaveInstan

java.lang.IllegalStateException: Fragment already added:

Fragment 相关API getSupportFragmentManager().popBackStack() getFragmentManager().getBackStackEntryCount 其是操作谁的,当一个activity初次显示的时候,其是0还是1. Fragment 具体有几个stack, 前端界面显示stack, 和后端缓存的stack backStack 其属于哪一个stack Activity, fragment, stack 其之间的关联是什么 FragmentMa

博客维护停止,需要的伙伴们移步http://blog.csdn.net/panhouye

两个博客的维护着实费心,方便大家共同学习.督促.进步.感兴趣的伙伴们移步CSDN博客:http://blog.csdn.net/panhouye,博客目录如下: 1.Android中通过实现线程更新ProgressDialog(对话进度条) http://blog.csdn.net/panhouye/article/details/53300233 2.Android中的AlertDialog使用示例一(警告对话框) http://blog.csdn.net/panhouye/article/d

Android快速开发之appBase——(4).详解com.snicesoft.Application和BaseActivity

Android快速开发之appBase--(4).详解com.snicesoft.Application和BaseActivity 在Android快速开发之appBase--(1).appBase介绍中使用过com.snicesoft.Application和BaseActivity,本篇则解开她们的面纱. 1. com.snicesoft.Application 1) 源码分析 package com.snicesoft; import java.util.ArrayList; import

Android - Fragment BackStack 清空

Fragment BackStack 清空 int backStackCount = getFragmentManager().getBackStackEntryCount(); for(int i = 0; i < backStackCount; i++) { getFragmentManager().popBackStack(); }

Android Fragment add/replace以及backstack

无论Fragment以何种方式加入,都不会影响backstack,backstack由addToBackStack函数决定,只有调用了这个函数,才会将Fragment加入返回栈. 那么add和replace的区别是什么呢? 以add方式加入Fragment,底层的Fragment被覆盖,当按下返回键时,当前Fragment弹出返回栈,底层Fragment直接显示出来,无需重建. 以replace方式加入Fragment,底层Fragment被替换(replace嘛),当按下返回键时,当前Frag

Fragment基础操作

Fragment和Activity类似,同样是具备UI的属性:也就是都能用于规划UI布局... Building a Dynamic UI with Fragments --> Fragments具备有动态UI的属性.为了在Android上为用户提供动态的.多窗口的交互体验,我们需要将UI组件和Activity操作封装成模块进行使用,使得我们可以在activity中对这些模块进行切入切出操作.可以用Fragment来创建这些模块,Fragment就像一个嵌套的activity,拥有自己的布局(l

Android Fragment 的使用,一些你不可不知的注意事项

Fragment,俗称碎片,自 Android 3.0 开始被引进并大量使用.然而就是这样耳熟能详的一个东西,在开发中我们还是会遇见各种各样的问题,层出不穷.所以,是时候总结一波了. Fragment 简介 作为 Activity 界面的一部分,Fragment 的存在必须依附于 Activity,并且与 Activity 一样,拥有自己的生命周期,同时处理用户的交互动作.同一个 Activity 可以有一个或多个 Fragment 作为界面内容,并且可以动态添加.删除 Fragment,灵活控