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加入回退栈。一个点击后将新加入的TestFragment从Fragment的回退栈中弹出。
要让添加的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。再保存当前Fragment的tag名。现在我们注意到。在最外层有一个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的入栈操作完毕。现在开始弹栈分析:
如果你不想自己写弹栈的操作。可以直接按手机的返回按钮。也可以达到弹栈的效果。点击返回按键。会走Activity的onBackPress()方法:
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 里面的语句块。代表弹出当前栈中最上层的Fragment。else里的语句块。代表将栈中tag值为name的fragment及以上的全都从栈中弹出。由于不管是第一种还是第二种。实际上都是弹栈。在此处我们就只分析弹最上层的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。所以会依次走RESUMED、STARTED、STOPPED、ACTIVITY_CREATED、CREATED。在这里面。程序会走完剩下的生命周期:
在第9行f.performStop()中会执行Fragment的onStop()方法。第32行f.performDestroyView()执行onDestroyView()方法。第92行f.performDestroy()执行onDestroy()方法。95行f.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; } }
可以看见。这里又再次调用了FragmentManagerImpl的moveToState方法。在这里。如果之前移除的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); } }
至此整个弹栈操作结束!
- Fragment的回退栈管理分析
- (5)Fragment回退栈的管理
- fragment嵌套fragment的管理
- 深入分析动态管理Fragment
- Android源码分析之Fragment的View管理
- Fragment学习之回退栈的管理
- 对于activity与fragment回退栈的管理
- fragment的管理
- fragment的管理
- Fragment回退栈管理
- Fragment回退栈管理
- Fragment的使用分析
- [Android]Fragment源码分析(肆) Fragment栈管理
- Android 深入分析动态管理Fragment
- 安卓动态管理Fragment深入分析
- Fragment管理的工具类
- Fragment的BackStack管理过程
- 对Fragment的封装和fragment的显示隐藏回退栈管理
- QT学习6:字符串处理和编码相关
- bzoj2160: 拉拉队排练
- MATLAB 小知识 第二篇 元胞和构架
- ActiveMQ安装部署(Windows)
- 【Java 多线程】简单生产者消费者模型
- Fragment的回退栈管理分析
- Spring IoC
- 日志清理脚本
- 仿酷狗音乐播放器开发日志十三——左侧功能块的完善
- 恢复健康的是好久开始的话剪卡健康公司电话
- HDU 1061 Rightmost Digit
- python 类型和对象 (Shalabh Chaturvedi)
- 图表展示说明一二
- 仿酷狗音乐播放器开发日志十二——播放列表的实现六