Fragment的回退栈管理分析

来源:互联网 发布:优酷网络大电影分成 编辑:程序博客网 时间:2024/05/16 08:07

今天准备写写Fragment的回退栈管理策略。

 

为了更好的理解本篇博客的内容。大家最好先看看我的另一篇博客:Fragment加载过程分析。

下面是Fragment的回退栈使用方法的代码:


  @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);findViewById(R.id.addBackStack).setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {//添加Fragment到id为root的ViewGroup。并将其加入回退栈getSupportFragmentManager().beginTransaction().addToBackStack(null).add(R.id.root, new TestFragment()).commit();}});findViewById(R.id.popBackStack).setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {//从回退栈里弹出最新加入的FragmentgetSupportFragmentManager().popBackStackImmediate();}});}


在上面的代码中。我在界面加入两个按钮。一个点击后将TestFragment加入回退栈。一个点击后将新加入的TestFragmentFragment的回退栈中弹出。

 

要让添加的Fragment加入回退栈。需要记住,调用addToBackStack(String)方法必须在commit()方法之前。让Fragment从回退栈中弹栈的方法。有两种。一种就是上面用的popBackStackImmediate()方法。另一种是popBackStack(),它们的区别稍微会有详细的讲解。

 

首先说让Fragment的入栈操作。需要在commit()方法前调用addToBackStack()方法。下面我们来看此方法。此方法定义在BackStackRecord类中。它是FragmentTransaction的子类。也就是用于Fragment事务管理的类。


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。再保存当前Fragmenttag名。现在我们注意到。在最外层有一个mAllowAddToBackStack标志位。代表是否允许加入回退栈。若你不想它加入回退栈。可以调用 disallowAddToBackStack()方法,接着在我们提交此次添加操作时。调用了commitInternal()方法。它的调用层次为commit()--->commitInternal()


int commitInternal(boolean allowStateLoss) {        if (mCommitted) throw new IllegalStateException("commit already called");        if (FragmentManagerImpl.DEBUG) {            Log.v(TAG, "Commit: " + this);            LogWriter logw = new LogWriter(TAG);            PrintWriter pw = new PrintWriter(logw);            dump("  ", null, pw, null);        }        mCommitted = true;        if (mAddToBackStack) {            mIndex = mManager.allocBackStackIndex(this);        } else {            mIndex = -1;        }        mManager.enqueueAction(this, allowStateLoss);        return mIndex;    }

在第11行。当此Fragment要求入栈的时候。会由FargmentManager为它分配一个索引下标值。代表此次提交会将此组Fragment放于栈中的第几层。最下层的索引下标为0。接着往下。到BackStackRecord类中的run方法中:


  public void run() {  ......        if (mAddToBackStack) {            mManager.addBackStackState(this);        }  }

这里直接调用addBackStackState()方法。跟进去!


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

这里就是入栈的操作。入栈后调用reportBackStackChanged()方法。


void reportBackStackChanged() {        if (mBackStackChangeListeners != null) {            for (int i=0; i<mBackStackChangeListeners.size(); i++) {                mBackStackChangeListeners.get(i).onBackStackChanged();            }        }}

设置回退栈的监听器在addOnBackStackChangedListener()中。

public void addOnBackStackChangedListener(OnBackStackChangedListener listener) {        if (mBackStackChangeListeners == null) {            mBackStackChangeListeners = new ArrayList<OnBackStackChangedListener>();        }        mBackStackChangeListeners.add(listener);  }

到此。Fragment的入栈操作完毕。现在开始弹栈分析:

 

如果你不想自己写弹栈的操作。可以直接按手机的返回按钮。也可以达到弹栈的效果。点击返回按键。会走ActivityonBackPress()方法:


public void onBackPressed() {        if (!mFragments.popBackStackImmediate()) {            finish();        }    }

可以发现。在你按返回键的时候。它其实也是调用的popBackStackImmediate()方法。上面我们说到弹栈有两种方法。下面分列出来:

@Override    public void popBackStack() {        enqueueAction(new Runnable() {            @Override public void run() {                popBackStackState(mActivity.mHandler, null, -1, 0);            }        }, false);    }    @Override    public boolean popBackStackImmediate() {        checkStateLoss();        executePendingTransactions();        return popBackStackState(mActivity.mHandler, null, -1, 0);    }

上面的popBackStack()方法。它调用enqueueAction方法。将弹栈操作放入一个Runnable对象中。最后将此Runnable对象放入主线程的消息队列尾部。使主线程在处理完其他操作后再处理弹栈操作。而第二种。就直接开始弹栈了。接着跟进popBackStackState():FragmentManagerImpl类中:

  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) {              int last = mBackStack.size()-1;              if (last < 0) {                  return false;              }              final BackStackRecord bss = mBackStack.remove(last);              bss.popFromBackStack(true);              reportBackStackChanged();          } else {              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++) {                  if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i));                  states.get(i).popFromBackStack(i == LAST);              }              reportBackStackChanged();          }          return true;      }

这里有两个分支。if 里面的语句块。代表弹出当前栈中最上层的Fragmentelse里的语句块。代表将栈中tag值为namefragment及以上的全都从栈中弹出。由于不管是第一种还是第二种。实际上都是弹栈。在此处我们就只分析弹最上层的Fragment。为了方便阅读。将里面的代码块移出来:

  int last = mBackStack.size()-1;  if (last < 0) {     return false;  }  final BackStackRecord bss = mBackStack.remove(last);  bss.popFromBackStack(true);  reportBackStackChanged();


5行拿到栈中最上层的事务管理实例。提交时的Fragment都是放在此类中的Op类中的。第6行就是弹栈操作了:

  public void popFromBackStack(boolean doStateMove) {          Op op = mTail;          while (op != null) {              switch (op.cmd) {                  case OP_ADD: {                      Fragment f = op.fragment;                      f.mNextAnim = op.popExitAnim;                      mManager.removeFragment(f,                              FragmentManagerImpl.reverseTransit(mTransition),                              mTransitionStyle);                  } break;                  case OP_REPLACE: {                      Fragment f = op.fragment;                      if (f != null) {                          f.mNextAnim = op.popExitAnim;                          mManager.removeFragment(f,                                  FragmentManagerImpl.reverseTransit(mTransition),                                  mTransitionStyle);                      }                      if (op.removed != null) {                          for (int i=0; i<op.removed.size(); i++) {                              Fragment old = op.removed.get(i);                              old.mNextAnim = op.popEnterAnim;                              mManager.addFragment(old, false);                          }                      }                  } break;                  case OP_REMOVE: {                      Fragment f = op.fragment;                      f.mNextAnim = op.popEnterAnim;                      mManager.addFragment(f, false);                  } break;                  case OP_HIDE: {                      Fragment f = op.fragment;                      f.mNextAnim = op.popEnterAnim;                      mManager.showFragment(f,                              FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);                  } break;                  case OP_SHOW: {                      Fragment f = op.fragment;                      f.mNextAnim = op.popExitAnim;                      mManager.hideFragment(f,                              FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);                  } break;                  case OP_DETACH: {                      Fragment f = op.fragment;                      f.mNextAnim = op.popEnterAnim;                      mManager.attachFragment(f,                              FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);                  } break;                  case OP_ATTACH: {                      Fragment f = op.fragment;                      f.mNextAnim = op.popEnterAnim;                      mManager.detachFragment(f,                              FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);                  } break;                  default: {                      throw new IllegalArgumentException("Unknown cmd: " + op.cmd);                  }              }                op = op.prev;          }          if (doStateMove) {              mManager.moveToState(mManager.mCurState,                      FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle, true);          }          if (mIndex >= 0) {              mManager.freeBackStackIndex(mIndex);              mIndex = -1;          }      }

在里面的switch语句块中。不管你入栈的时候是以add还是以replace方式入的。都会调用FragmentManagerImpl中的removeFragment方法。

  public void removeFragment(Fragment fragment, int transition, int transitionStyle) {          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;              moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,                      transition, transitionStyle, false);          }  }

可以看见。在方法内部。会将需要移除的fragment的状态值重新设为INITIALIZING,这时原inactive变量为true:直接跟进:

  void moveToState(Fragment f, int newState, int transit, int transitionStyle,              boolean keepActive) {         ......          else if (f.mState > newState) {              switch (f.mState) {                  case Fragment.RESUMED:                      if (newState < Fragment.RESUMED) {                          if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);                          f.performPause();                          f.mResumed = false;                      }                  case Fragment.STARTED:                      if (newState < Fragment.STARTED) {                          if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);                          f.performStop();                      }                  case Fragment.STOPPED:                      if (newState < Fragment.STOPPED) {                          if (DEBUG) Log.v(TAG, "movefrom STOPPED: " + f);                          f.performReallyStop();                      }                  case Fragment.ACTIVITY_CREATED:                      if (newState < Fragment.ACTIVITY_CREATED) {                          if (DEBUG) Log.v(TAG, "movefrom ACTIVITY_CREATED: " + f);                          if (f.mView != null) {                              // Need to save the current view state if not                              // done already.                              if (!mActivity.isFinishing() && f.mSavedViewState == null) {                                  saveFragmentViewState(f);                              }                          }                          f.performDestroyView();                          if (f.mView != null && f.mContainer != null) {                              Animation anim = null;                              if (mCurState > Fragment.INITIALIZING && !mDestroyed) {                                  anim = loadAnimation(f, transit, false,                                          transitionStyle);                              }                              if (anim != null) {                                  final Fragment fragment = f;                                  f.mAnimatingAway = f.mView;                                  f.mStateAfterAnimating = newState;                                  anim.setAnimationListener(new AnimationListener() {                                      @Override                                      public void onAnimationEnd(Animation animation) {                                          if (fragment.mAnimatingAway != null) {                                              fragment.mAnimatingAway = null;                                              moveToState(fragment, fragment.mStateAfterAnimating,                                                      0, 0, false);                                          }                                      }                                      @Override                                      public void onAnimationRepeat(Animation animation) {                                      }                                      @Override                                      public void onAnimationStart(Animation animation) {                                      }                                  });                                  f.mView.startAnimation(anim);                              }                              f.mContainer.removeView(f.mView);                          }                          f.mContainer = null;                          f.mView = null;                          f.mInnerView = null;                      }                  case Fragment.CREATED:                      if (newState < Fragment.CREATED) {                          if (mDestroyed) {                              if (f.mAnimatingAway != null) {                                  // The fragment's containing activity is                                  // being destroyed, but this fragment is                                  // currently animating away.  Stop the                                  // animation right now -- it is not needed,                                  // and we can't wait any more on destroying                                  // the fragment.                                  View v = f.mAnimatingAway;                                  f.mAnimatingAway = null;                                  v.clearAnimation();                              }                          }                          if (f.mAnimatingAway != null) {                              // We are waiting for the fragment's view to finish                              // animating away.  Just make a note of the state                              // the fragment now should move to once the animation                              // is done.                              f.mStateAfterAnimating = newState;                              newState = Fragment.CREATED;                          } else {                              if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);                              if (!f.mRetaining) {                                  f.performDestroy();                              }                              f.mCalled = false;                              f.onDetach();                              if (!f.mCalled) {                                  throw new SuperNotCalledException("Fragment " + f                                          + " did not call through to super.onDetach()");                              }                              if (!keepActive) {                                  if (!f.mRetaining) {                                      makeInactive(f);                                  } else {                                      f.mActivity = null;                                      f.mFragmentManager = null;                                  }                              }                          }                      }              }          }                    f.mState = newState;      }

这次由于是要做弹栈操作。所以我们直接看f.mState > newState的语句块。此时的f.mState状态值为RESUMED,要到状态INITIALIZING。所以会依次走RESUMEDSTARTEDSTOPPEDACTIVITY_CREATEDCREATED。在这里面。程序会走完剩下的生命周期:

 

在第9f.performStop()中会执行FragmentonStop()方法。第32f.performDestroyView()执行onDestroyView()方法。第92f.performDestroy()执行onDestroy()方法。95f.onDetach()执行onDetach()方法与Activity解除绑定。至此。生命周期走完全程。最后在102行的 makeInactive(f)中对当前的fragment重新初始化各种状态值。

真正将此Fragment中的视图从界面移除的地方在第61行。f.mContainer.removeView(f.mView),从上篇博客我们知道。f.mContainer就是入栈时add参数中第一个的布局ID对应的ViewGroup的实例。f.mView就是Fragment中在onCreateView()中返回的填充出来的View视图。

最后回到popFromBackStack中。在removeFragment后。FragmentManager会要求上一次加入栈中的Fragment将其状态移动到RESUMED状态。为了方便分析。我们将代码移下来。

public void popFromBackStack(boolean doStateMove) {  ......        if (doStateMove) {            mManager.moveToState(mManager.mCurState,                    FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle, true);        }        if (mIndex >= 0) {            mManager.freeBackStackIndex(mIndex);            mIndex = -1;        }  }


可以看见。这里又再次调用了FragmentManagerImplmoveToState方法。在这里。如果之前移除的Fragment的入栈方式是以replace方式入栈的。则此时栈顶层的Fragment的状态值为INITIALIZING,也就是此时在moveToState中又会重新将此Fragment的创建一次。重新走一遍创建时的生命周期。如果是以add方式入栈的。则此Fragment的状态仍是RESUMED。则moveToState里面就将不进行操作。

当状态移动完毕。会接着调用freeBackStackIndex释放回退栈中顶层的索引下标。便于下次入栈时得到正确的下标值:

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.add(index);        }  }

至此整个弹栈操作结束!
2 0