源码分析Fragmentd的BackStack管理过程
来源:互联网 发布:windows测试udp端口 编辑:程序博客网 时间:2024/06/06 19:31
为了管理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类的实例:
FragmentManager fragmentManager = getFragmentManager();FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
每个transaction是一组同时执行的变化的集合。用add(), remove(), replace()方法,把所有需要的变化加进去,然后调用commit()方法,将这些变化应用。在commit()方法之前,你可以调用addToBackStack(),把这个transaction加入back stack中去,这个back stack是由activity管理的,当用户按返回键时,就会回到上一个fragment的状态。下面的代码非常典型,用一个新的fragment取代之前的fragment,并且将之前的状态存储在back stack中。
// Create new fragment and transactionFragment newFragment = new ExampleFragment();FragmentTransaction transaction = getFragmentManager().beginTransaction();// Replace whatever is in the fragment_container view with this fragment,// and add the transaction to the back stacktransaction.replace(R.id.fragment_container, newFragment);transaction.addToBackStack(null);// Commit the transactiontransaction.commit();通过调用addToBackStack(),commit()的一系列转换作为一个transaction被存储在back stack中,用户按Back键可以返回上一个转换前的状态。
调用commit()方法并不能立即执行transaction中包含的改变动作,commit()方法把transaction加入activity的UI线程队列中。
下面我们对上述代码中出现的函数进行分析,以此来逐步学习Fragment的管理机制。
getSupportFragmentManager():
public FragmentManager getSupportFragmentManager() { return mFragments;}该函数返回类型是FragmentManager,FragmentManager是一个抽象类,其实现类是FragmentManager.FragmentManagerImpl
beginTransaction():
该函数在FragmentManagerIMpl中的源码如下:
public FragmentTransaction beginTransaction() {return new BackStackRecord(this);}返回一个BackStackRecord对象,该对象是FragmentTranscation的一个子类。
BackStackRecord的声明如下:
final class BackStackRecord extends FragmentTransaction implements FragmentManager.BackStackEntry, Runnable {...}该类实现了一个重要的接口:FragmentManager.BackStackEntry, 该接口代表了fragment back stack的一个入口。可以用FragmentManager.getBackStackEntry()来检索BackStackEntry。
接下来执行transaction.replace(), 查看BackStackRecord,调用过程源码如下:
public FragmentTransaction replace(int containerViewId, Fragment fragment) {return replace(containerViewId, fragment, null);}public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {if (containerViewId == 0) {throw new IllegalArgumentException("Must use non-zero containerViewId");}doAddOp(containerViewId, fragment, tag, OP_REPLACE);return this;}我们发现,replace()最终调用的函数为doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd), 将Fragment和对Fragment所进行的操作放到op链表中:
private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {fragment.mFragmentManager = mManager;if (tag != null) {if (fragment.mTag != null && !tag.equals(fragment.mTag)) {throw new IllegalStateException("Can't change tag of fragment "+ fragment + ": was " + fragment.mTag+ " now " + tag);}fragment.mTag = tag;}if (containerViewId != 0) {if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {throw new IllegalStateException("Can't change container ID of fragment "+ fragment + ": was " + fragment.mFragmentId+ " now " + containerViewId);}fragment.mContainerId = fragment.mFragmentId = containerViewId;}Op op = new Op();op.cmd = opcmd;op.fragment = fragment;addOp(op);}该函数首先设置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()如下:
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++;}该函数将Op对象添加到链表的末尾,并将mNumOp的值增一。
transaction.addToBackStack(null)设置了mAddToBackStack为true,源码如下:
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,并设置mName字段。
最后调用transaction.commit()来执行transaction。commit()的调用过程代码如下:
public int commit() {return commitInternal(false);}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;}由于mAddToBackStack为true,所以会用FragmentManager为BackstackRecorder也即FragmentTransaction分配一个index,分配过程如下:
public int allocBackStackIndex(BackStackRecord bse) {synchronized (this) {if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) {if (mBackStackIndices == null) {mBackStackIndices = new ArrayList<BackStackRecord>();}int index = mBackStackIndices.size();if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);mBackStackIndices.add(bse);return index;} else {int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1);if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);mBackStackIndices.set(index, bse);return index;}}}FragmentManager用mAvailBackStackIndices和mBackStackIndices两个数组来为BackStackRecord分配Index。mAvailBackStackIndices用来存储在mBackStackIndices中能够分配的Index,mBackStackIndices则用来保存BackStackRecord。这利用两个数组可以减少对mBackStackIndices的动态分配大小的次数,是一个以空间换时间的策略。上面的代码首先判断是否有可用的Index分配给BackStackRecord,若无则直接将BackStackRecord插入到mBackStackIndices;若存在的话则从mAvailBackStackIndices的队尾取出一个index,然后设置mBackStackIndices中该index下的值。
让我们回到commit()中,该函数最后执行mManager.enqueAction(),源码如下:
public void enqueueAction(Runnable action, boolean allowStateLoss) {if (!allowStateLoss) {checkStateLoss();}synchronized (this) {if (mDestroyed || mActivity == null) {throw new IllegalStateException("Activity has been destroyed");}if (mPendingActions == null) {mPendingActions = new ArrayList<Runnable>();}mPendingActions.add(action);if (mPendingActions.size() == 1) {mActivity.mHandler.removeCallbacks(mExecCommit);mActivity.mHandler.post(mExecCommit);}}}
该函数首先进行状态监测,查看该Fagment所在的Activity的生命周期是否处于Saving Activity之前,因为Activity保存状态往往是由用户离开那个Activity所造成的,在此之后执行commit会丢失一些状态信息。针对这种情况,可以使用commitAllowingStateLoss().最后将BackStackRecord加入到执行队列中。当第一次往执行队列中添加消息时,首先会从消息队列中所有callback属性为mExecCommit的消息删除,然后重新将mExecCommit添加到消息队列。mExecCommit的定义如下:
Runnable mExecCommit = new Runnable() {@Overridepublic void run() {execPendingActions();}};execPendingActions()只能在主线程内被调用,其内部通过一个循环对mPendingActions中的Actions进行执行。值得注意的是,每执行一次循环,mPendingActions中的所有Action都会被添加到一个临时数组中,然后这个数组被变量一遍以执行数组中的每个Runnable。同时,每个Runnable直接被调用了run,而不是开个线程执行的。当这个Runnable在执行的时候,mPendingActions数组可能会被添加内容。当某一时刻mPendingActions中的内容为空,则while循环退出。此部分代码如下:
public boolean execPendingActions() { if (mExecutingActions) { throw new IllegalStateException("Recursive entry to executePendingTransactions"); } if (Looper.myLooper() != mActivity.mHandler.getLooper()) { throw new IllegalStateException("Must be called from main thread of process"); } boolean didSomething = false; while (true) { int numActions; synchronized (this) { if (mPendingActions == null || mPendingActions.size() == 0) { break; } numActions = mPendingActions.size(); if (mTmpActions == null || mTmpActions.length < numActions) { mTmpActions = new Runnable[numActions]; } mPendingActions.toArray(mTmpActions); mPendingActions.clear(); mActivity.mHandler.removeCallbacks(mExecCommit); } //一次性执行完数组中所有的Action mExecutingActions = true; for (int i=0; i<numActions; i++) { mTmpActions[i].run(); mTmpActions[i] = null; } mExecutingActions = false; didSomething = true; } if (mHavePendingDeferredStart) { boolean loadersRunning = false; for (int i=0; i<mActive.size(); i++) { Fragment f = mActive.get(i); if (f != null && f.mLoaderManager != null) { loadersRunning |= f.mLoaderManager.hasRunningLoaders(); } } if (!loadersRunning) { mHavePendingDeferredStart = false; startPendingDeferredFragments(); } } return didSomething;}由于BackstackRecorder实现了Runnable,我们来看看BackStackRecorder中的run(),如下所示:
public void run() {if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Run: " + this);if (mAddToBackStack) {if (mIndex < 0) {throw new IllegalStateException("addToBackStack() called after commit()");}}bumpBackStackNesting(1);Op op = mHead;//遍历op,根据cmd的类型对Fragment和FragmentManager进行相应的设置while (op != null) {switch (op.cmd) {case OP_ADD: {Fragment f = op.fragment;f.mNextAnim = op.enterAnim;//将Fragment添加到FragmentManager中,其源码显示是将Fragment添加到FragmentManager中的mActive数组中,并将Fragment添加到了数组mAdded中。mManager.addFragment(f, false);} break;case OP_REPLACE: {Fragment f = op.fragment;if (mManager.mAdded != null) {//遍历已经添加的Fragment,for (int i=0; i<mManager.mAdded.size(); i++) {Fragment old = mManager.mAdded.get(i);if (FragmentManagerImpl.DEBUG) Log.v(TAG,"OP_REPLACE: adding=" + f + " old=" + old);//如果发现两个mContainerId一样,则进行特殊处理if (f == null || old.mContainerId == f.mContainerId) {if (old == f) {//两个Fragment一样,则置空,保留old中的Fragmentop.fragment = f = null;} else {// 将old fragment加入到 op.removed数组中,保留op中的Fragmentif (op.removed == null) {op.removed = new ArrayList<Fragment>();}op.removed.add(old);old.mNextAnim = op.exitAnim;if (mAddToBackStack) {//设置old Fragment在BackStack中的Numberold.mBackStackNesting += 1;if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "+ old + " to " + old.mBackStackNesting);}//对old Fragment设置相应的状态属性,如mAdded、mRemoving, 从FragmentManager中移除oldFrgment的相关属性mManager.removeFragment(old, mTransition, mTransitionStyle);}}}}//将Fragment添加到FragmentManager中if (f != null) {f.mNextAnim = op.enterAnim;mManager.addFragment(f, false);}} break;case OP_REMOVE: ......}op = op.next;}//设置Fragment的当前状态,然后根据当前状态来回调Fragment的生命周期中的相关函数。此函数控制了Fragment的生命周期和Fragment的绘制,想要彻底理解Fragment的生命周期的同学可以认真研究此函数。mManager.moveToState(mManager.mCurState, mTransition,mTransitionStyle, true);//将BackStackRecord加入到BackStack中,并回调onBackStackChangedif (mAddToBackStack) {mManager.addBackStackState(this);}}addBackStackState()的源码如下:
void addBackStackState(BackStackRecord state) {if (mBackStack == null) {mBackStack = new ArrayList<BackStackRecord>();}mBackStack.add(state);//回调onBackStackChangedreportBackStackChanged();}可以看到传说中的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的时候被调用。下次我们将深入这个函数。
- 源码分析Fragmentd的BackStack管理过程
- 源码分析Fragmentd的BackStack管理过程
- 源码分析Fragmentd的BackStack管理过程
- 源码分析Fragmentd的BackStack管理过程
- Fragment的BackStack管理过程
- android Fragmentd的使用
- solrcloud集群启动管理过程基于源码的分析
- BackStack
- Android中Activity的启动流程和组织管理方式(Backstack、Task)
- Android输入管理服务启动过程源码分析
- 分析AsyncTask的源码的过程
- springmvc的执行过程的源码分析
- 逆向分析并重组源码的过程
- U-Boot的启动过程源码分析
- struts2的请求处理过程源码分析
- struts2的请求处理过程源码分析
- spring源码分析,view的处理过程
- Android源码分析-Activity的启动过程
- Unity3D移动端内存优化(NGUI方面)
- 自然语言处理(NLP)网上资源整理3
- thinkphp 如何给控制器传值,实现按钮删除数据库信息
- MFC学习笔记四 用鼠标设定文字显示开始位置
- 求a+b的程序
- 源码分析Fragmentd的BackStack管理过程
- 使用matlab时,将C++编译成matlab时出现错误
- C/C++获取当前系统时间
- http://blog.csdn.net/xiancaifu/article/details/6076289
- linux 学习之路----编译
- 多区域显示(5)-粘贴为图片链接
- 四个步骤实现在ESRI ArcMap中加载17.6G离线卫星地图的方法
- ubuntu降级php(默认安装apache,php,mysql)
- .NET中string和StringBuilder的区别