Fragment运行机制源码分析(二)
来源:互联网 发布:linux ddos攻击脚本 编辑:程序博客网 时间:2024/06/05 02:48
尊重原创:http://blog.csdn.net/yuanzeyao/article/details/52895029
如果你喜欢我的文章,可以关注左边微信公众号,我会定期推送文章跟新。
在前面的文章中,我们详细分析了Fragment的生命周期,以及讲解了Activity如何控制Fragment的生命周期,按照计划,本篇文章就来分析一下add
,replace
,remove
,hide
,show
等api到底做了生命逻辑,如果大家还没有阅读Fragment运行机制分析(一),那么建议先阅读一下。
在具体分析源码之前,我们来看看平时我们是如何使用这些api的,已add
为例:
FragmentManager mFragmentManager = getSupportFragmentManager(); mFragmentManager.beginTransaction() .add(R.id.fl_container,HomeFragment.newInstance(), HomeFragment.class.getSimpleName()) .commit();
我们发现,向Activity中添加一个Fragment非常简单,就4步:
- 获取Activity中的
FragmentManager
,通过上篇文章分析,我们知道返回的是FragmentManagerImpl
对象. - 调用
beginTransaction()
返回一个FragmentTransaction,它仅仅是一个抽象类,后面我们会分析一下它的子类. - 调用add方法,第一个参数的Fragment将要加入的容器id,第二个参数是要添加的Fragment,第三个参数是此Fragment的tag,设置了这个tag之后,后面就可以通过方法
findFragmentByTag
方法找到此Fragment,主要用于Fragment的自动恢复. 执行
commit
方法.同理
replace
,remove
,hide
,show
等api的调用方式都是一模一样(当然有时也可能会调用addToBackStack
).
接下来,我们从第2步开始研究每一步做了什么,进入FragmentManagerImpl
,查看beginTransaction
方法逻辑:
@Override public FragmentTransaction beginTransaction() { return new BackStackRecord(this); }
看到这里大家就知道FragmentTransaction
的实现类就是BackStackRecord
,我们接下来看看此类的继承关系:
final class BackStackRecord extends FragmentTransaction implements FragmentManager.BackStackEntry, Runnable
你会发现BackStackRecord
不仅继承了FragmentTransaction
类,而且还实现了Runnable
接口,说明BackStackRecord
应该是一个线程.
在第2步拿到了FragmentTransaction
之后就调用add
方法,我们进入BackStackRecord
的add
方法:(Note:add
方法有多个重载,但是里面的逻辑基本一致,所以我只分析其中一个)
public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) { doAddOp(containerViewId, fragment, tag, OP_ADD); return this; }
原来add
方法仅仅调用了一个doAddOp
方法,我们通过这个名字可以猜想一下,这个此方法的功能:添加一个Add 操作。add
操作返回this
说明支持链式调用。
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); }
我们接下来仔细分析这个方法:
- 将
FragmentManagerImpl
对象保存到Fragment.mFragmentManager
中. - 检查
Fragment
是否设置过tag,如果设置过,并且和参数中的tag不一致,那么抛出异常。如果没有设置过,那么保存tag. - 检查
Fragment
,如果Fragment
中的containerId已经不等于0,并且和参数的containerId不一致,那么抛出异常,如果等于0,那么保存containerId - 创建
Op
对象,在此对象保存cmd参数和Fragment
参数,这里cmd 的值为OP_ADD
. 调用
addOp
方法,并将上面创建的Op
对象传入.我们顺藤摸瓜,进入
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
对象加入链表的尾部。ok,add
方法分析完毕,原来add方法仅仅是创建了一个”添加操作”,然后放入链表尾部,接下来,我们分析最后一步:commit方法:
public int commit() { return commitInternal(false); }
太简单,不用解释,直接进入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; }
此方法的逻辑也是非常清晰:首先检查commit
方法是否已经执行过,如果多次执行此commit
方法就会抛出异常。判断是否调用过addBackToStack
(我们这里没有调用),如果调用了那么需要由FragmentManager
分配一个index,否则返回-1,最后调用mManager.enqueueAction(this, allowStateLoss);
方法.
这里我们看到如果在调试阶段,我们想看FragmentManager的相关Log,我们可以调用FragmentManager的enableDebugLogging方法将Log打开,并通过FragmentManager过滤.
接下来进入FragmentManagerImpl.enqueueAction
方法看看.
public void enqueueAction(Runnable action, boolean allowStateLoss) { if (!allowStateLoss) { checkStateLoss(); } synchronized (this) { if (mDestroyed || mHost == null) { throw new IllegalStateException("Activity has been destroyed"); } if (mPendingActions == null) { mPendingActions = new ArrayList<Runnable>(); } mPendingActions.add(action); if (mPendingActions.size() == 1) { mHost.getHandler().removeCallbacks(mExecCommit); mHost.getHandler().post(mExecCommit); } } }
这个方法也很简单,首先检查是否允许状态丢失,这里我们传入的false,所以首先调用checkStateLoss
,此方法我就不分析,其实就是检查onSaveInstanceState
方法是否已经执行,如果执行了,那么会抛出异常。检查通过后将Runnable
加入一个待执行列表mPendingActions
(根据前面的分析我们知道这个Runnable
就是BackStackRecord
对象)。紧接着将mExecCommit
放入Handler
中,其实mExecCommit
也是一个Runnable
,它的run
方法如下:
Runnable mExecCommit = new Runnable() { @Override public void run() { execPendingActions(); } };
run
方法就是调用了execPendingActions
方法,逻辑如下:
/** * Only call from main thread! */ public boolean execPendingActions() { if (mExecutingActions) { throw new IllegalStateException("Recursive entry to executePendingTransactions"); } if (Looper.myLooper() != mHost.getHandler().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(); mHost.getHandler().removeCallbacks(mExecCommit); } 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; }
其实就是执行BackStackRecord
对吧,所以我们需要进入BackStackRecord
的run方法研究一下,run方法比较长,里面有一个switch语句:
case OP_ADD: { Fragment f = op.fragment; f.mNextAnim = enterAnim; mManager.addFragment(f, false); } break;
我贴出来的case就是对应的OP_ADD操作的api,通过前面分析可以知道,当我们执行了add
操作之后,最终调用的是FragmentManager.addFragment
方法.
public void addFragment(Fragment fragment, boolean moveToStateNow) { if (mAdded == null) { mAdded = new ArrayList<Fragment>(); } if (DEBUG) Log.v(TAG, "add: " + fragment); makeActive(fragment); if (!fragment.mDetached) { if (mAdded.contains(fragment)) { throw new IllegalStateException("Fragment already added: " + fragment); } mAdded.add(fragment); fragment.mAdded = true; fragment.mRemoving = false; if (fragment.mHasMenu && fragment.mMenuVisible) { mNeedMenuInvalidate = true; } if (moveToStateNow) { moveToState(fragment); } } }
addFragment
的主要工作如下:
将当前
fragment
放入mActivie列表并分配一个index,这个功能是通过调用makeActive
实现void makeActive(Fragment f) { if (f.mIndex >= 0) { return; } if (mAvailIndices == null || mAvailIndices.size() <= 0) { if (mActive == null) { mActive = new ArrayList<Fragment>(); } f.setIndex(mActive.size(), mParent); mActive.add(f); } else { f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1), mParent); mActive.set(f.mIndex, f); } if (DEBUG) Log.v(TAG, "Allocated fragment index " + f);}
将
fragment
放入mAdd
列表,由于addFragment
的第二个参数传入的是false,所以此时不会执行moveToState
方法,(moveToState
在前面一篇文章已经详细分析了),那么此方法什么时候执行的呢?大家回到BackStackRecord
的run方法,最后面就会调用此方法mManager.moveToState(mManager.mCurState, transition, transitionStyle, true); if (mAddToBackStack) { mManager.addBackStackState(this); }
OK,到此为止详细
add
操作大家已经非常清楚了,使用同样的分析方法,我们来看看remove
的逻辑,你会发现remove
最终也是调用到了BackStackRecord
的run
方法,只不过对应的是下面的case:
case OP_REMOVE: { Fragment f = op.fragment; f.mNextAnim = exitAnim; mManager.removeFragment(f, transition, transitionStyle);
调用的是FragmentManager.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); } }
removeFragment
首先检查此fragment
是否调用过addBackToStack
,如果没有调用则inactive
为true检查
fragment.mDetached
或者inactive
是否为true,如果为true,那么将fragment
从mAdded
列表中删除,并调用moveToState
方法这里可能有些同学会有疑问:
moveToState
会不会执行两次:BackStackRecord.run
和removeFragmnet
方法都会调用,但是如果你仔细分析,你会发现当removeFragment
执行之后,Fragment
会从mActive
移除,所以第二次执行moveToState的时候
发现Fragment
为null,所以什么都不干。
至于其他的api(replace
,hide
,show
) 你们可以自己分析,其实逻辑是一样的,这里我就不分析了。
- Fragment运行机制源码分析(二)
- Fragment运行机制源码分析(二)
- Fragment运行机制源码分析(一)
- Fragment运行机制源码分析(一)
- Handler运行机制源码分析
- [Android]Fragment源码分析(二) 状态
- Fragment源码分析
- android Fragment 源码分析
- Fragment源码分析
- Fragment事务管理源码分析
- Fragment源码分析
- Android之JobScheduler运行机制源码分析
- seajs源码分析-运行机制浅析(一)
- Fragment实例化,Fragment生命周期源码分析
- 【OVS2.5.0源码分析】ovsd进程运行机制分析(1)
- Fragment源码分析(一) 构造
- 开源中国源码学习UI篇(二)之NavigationDrawer+Fragment的使用分析
- Java常用类源码分析及运行机制(一):logging
- 58. Length of Last Word
- 高并发网络编程之epoll详解
- Docker配置桥接网络
- java内存详解
- 测试数据库
- Fragment运行机制源码分析(二)
- 数组与指针2
- poj3398 Perfect Service
- linux 中php以及nginx的重启命令
- storyboard学习心得
- NOIP2016 滚粗记
- Java编程思想(第四章)
- 翻转链表
- 并查集详解