Fragment 是android开发中最常用的组件之一,用了好几年,我都不知道Fragment到底是个什么东西,Activity加载Fragment的原理是怎样的,为什么官方会叫它为碎片?直到前段时间因为工作需要,从头看来一遍Fragment的源代码,然后就有了本文。 本文将从commit开始一步步带你走向Fragmnt的生命周期!!
经典的Frgment加载 从最经典的Activity加载Fragment的流程说起,如下所示,是Activity加载Frgment的例子代码。
<LinearLayout xmlns:android ="http://schemas.android.com/apk/res/android" android:orientation ="horizontal" android:layout_width ="match_parent" android:layout_height ="match_parent" > <FrameLayout android:id ="@+id/content" android:layout_weight ="1" android:layout_width ="0px" android:layout_height ="match_parent" android:background ="?android:attr/detailsElementBackground" /> </LinearLayout >
public static class DetailsActivity extends Activity { @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); if (savedInstanceState == null ) { DetailsFragment details = new DetailsFragment (); details.setArguments(getIntent().getExtras()); getFragmentManager().beginTransaction().replace(R.id.content, details).commit(); } } }
Activity是通过getFragmentManager().beginTransaction()来进行加载Fragment的,我们就从这句话开始吧! fragment状态的切换时通过beginTransaction()方法来实现的,而beginTransaction()是FragmentManager的抽象方法,而FragmentManager真正的实现是FragamentManager的内部类FragmentManagerImpl
,因此在FragmentManagerImpl中查找beginTransaction方法,最终方法发现beginTransaction真正生成的实例是BackStackRecord
。
@Override public FragmentTransaction beginTransaction () { return new BackStackRecord (this ); }
由此可以知道replace,add,hide,show等等对Fragment状态的操作的真正实现都是在BackStackRecord的对应方法中实现,再看BackStackRecord的replace、add方法最终都会跳转到doAddOP
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); }
上面的代码中最核心的部分是最后的四行,可以看到内次replace都会new一个Op
,那么OP
到底是个什么东西?
static final class Op { Op next; Op prev; int cmd; Fragment fragment; int enterAnim; int exitAnim; int popEnterAnim; int popExitAnim; ArrayList<Fragment> removed; }
这是OP的源代码,可以看到OP有next和prev的字段,这两个字段的属性都是Op
本身,并且它本身还有一些自带的属性,到现在依然不知道这个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++; }
从if-else语句中,再结合Op的结构,可以看出Op
是一个双向链表,而addOp
方法的作用是将replce生成的Op
添加到当前链表中,到现在我们已经知道Op
是个双向链表了,如下图所示:
那么Op
到底有什么用,从目前看到的源码来看我们还是不知道。继续看BackStackRecord的show、remove等方法。
public FragmentTransaction remove (Fragment fragment) { Op op = new Op (); op.cmd = OP_REMOVE; op.fragment = fragment; addOp(op); return this ; } public FragmentTransaction hide (Fragment fragment) { Op op = new Op (); op.cmd = OP_HIDE; op.fragment = fragment; addOp(op); return this ; } public FragmentTransaction show (Fragment fragment) { Op op = new Op (); op.cmd = OP_SHOW; op.fragment = fragment; addOp(op); return this ; }
从上面的代码看出,无论是replcae、add还是remove、hide、show操作,都会new一个Op
并加到链表中,而每次添加链表时,Op的cmd和fragment都不一样,因此可以猜测Op链表实质上是操作fragment的命令链表,而执行命令的操作由commit等方法来完成的 继续看BackStackRecord的commit方法,在前面我们猜测fragment状态的控制是由这个方法完成的。
public int commit () { return commitInternal(false ); } public int commitAllowingStateLoss () { return commitInternal(true ); } 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的源代码,核心代码是倒数第二行,看到命令的执行最终还是有mManager控制,但是真的是这样吗?mManager到底是在哪个地方初始化的,还记得FragmentManagerImpl的beginTransaction
方法吗? 跳转到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); } } }
从这个方法可以看出,真正执行执行Op命令的是mExecCommit
这个线程,继续看mExecCommit
线程。
Runnable mExecCommit = new Runnable () { @Override public void run () { execPendingActions(); } }; 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; }
这是mExecCommit
线程真正执行的方法,但是还是看不到任何关于Fragment被操作的痕迹,也看不到任何的Op命令,继续看execPendingActions方法,在这个方法中,有个代码片段吸引了我的注意
for (int i=0 ; i<numActions; i++) { mTmpActions[i].run(); mTmpActions[i] = null ; } `` 在这我看到了run方法!!!而mTmpActions是mPendingActions的数组化,在`enqueueAction`中,mPendingActions是加载的是BackStackRecord!!!!!**而BackStackRecord是实现Runnable接口的!!**,现在一切都清楚了,真正执行Op命令的还是在BackStackRecord中。</br> 继续看BackStackRecord的run方法。 ```java while (op != null ) { ... switch (op.cmd) { ... case OP_ADD: { Fragment f = op.fragment; f.mNextAnim = enterAnim; mManager.addFragment(f, false ); } break ; case OP_HIDE: { Fragment f = op.fragment; f.mNextAnim = exitAnim; mManager.hideFragment(f, transition, transitionStyle); } break ; case OP_SHOW: { Fragment f = op.fragment; f.mNextAnim = enterAnim; mManager.showFragment(f, transition, transitionStyle); } break ; ... } op = op.next; }
上面是run的部分代码,到这里我们终于看到了希望见到的Op
命令!!在run方法中,while会遍历并执行Op链表中的所有命令,执行命令的过程最终还是通过mManager来执行,拿addFragment来示例,
public void addFragment (Fragment fragment, boolean moveToStateNow) { ... if (moveToStateNow) { moveToState(fragment); } }
从上面的代码看到真正控制Fragment状态的是moveToState方法,多次跳转后最终跳转到
void moveToState (Fragment f, int newState, int transit, int transitionStyle, boolean keepActive) { ... if (f.mState < newState) { ... switch (f.mState) { case Fragment.INITIALIZING: ... f.onAttach(mHost.getContext()); ... case Fragment.CREATED: ... f.mView = f.performCreateView(f.getLayoutInflater(f.mSavedFragmentState), container, f.mSavedFragmentState); f.performActivityCreated(f.mSavedFragmentState); container.addView(f.mView); ... case Fragment.ACTIVITY_CREATED: case Fragment.STOPPED: ... f.performStart(); ... case Fragment.STARTED: ... f.performResume(); ... } }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.CREATED: if (newState < Fragment.CREATED) { if (f.mAnimatingAway != null ) { f.mStateAfterAnimating = newState; newState = Fragment.CREATED; } else { if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f); if (!f.mRetaining) { f.performDestroy(); } } } } }
由于篇幅所限,上面的只是代码片段,从moveToState方法中,我们见到了Fragment的整个生命周期!!! 在第一个switch的case Fragment.CREATED:
中Fragment 的 view 的被加载,不知道你是否还记得文章最开始的那个xml中id为content的FrameLayout,在这个case下,fragment的view正是被加载到这个Layout中!!!!!! 这里有个有意思的地方,两个switch都是没有break语句的,当我们第一次add是,f.mState的默认状态为INITIALIZING,该状态下,fragemnt就要走完onAttach–onResume的所有流程,通过上面的代码,最终可以知道,fragment的全部生命周期都由mState这个字段决定。
到了现在getFragmentManager().beginTransaction().replace(R.id.content, details).commit();
这句话我们算是完全清楚了它的工作流程。
总结 在加载Fragament中,beginTransaction()创建了一个BackStackRecord对象,该对象实现了Runnable接口,replace方法将replace命令加载到BackStackRecord的Op链表中,当开发者调用commit方法时,commit方法将以事件的形式生成Op命令并将Op传递给FragmentManagerImpl,FragmentManagerImpl
在Activity的handler中启动mExecCommit线程,mExecCommit线程执行BackStackRecord线程,在BackStackRecord的run方法里面,遍历所有Op链表,依次执行Op链表中所有的命令,run的switch根据Op命令,动态执行调用FragmentManagerImpl的replace、add、show等方法,在FragmentManagerImpl中的replace、add、show等方法最终都会调用moveToState方法,而整个Fragment的生命周期都在这个moveToState方法中。在这方法中,在create中,fragment最终将被加载找eginTransaction().replace(R.id.content, details),replace第一个参数所指向的Layout中!!加载View完成后,moveToState将根据fragment的mState继续执行Fragment的生命周期。
到现在整个Fragment的生成加载,生命周期我们全部了解完成了,得出的最后一个结论是Fragment本质上是嵌入在Activity中一个ViewGroup的View,但是谷歌给这个View赋予了生命周期 ,看到Fragment后终于明白Square为什么要建议放弃Fragment了!!! 虽然感觉Square公司有点偏激,但是作为一般开发者,在能用自定义View的情况下还是尽量不要用Fragment,因为Fragment实在太复杂了,一旦出现奇怪的问题,根本找不到哪个地方出的错,说多了都是泪!!! 。