从commit一步步带你走向fragment的生命周期
来源:互联网 发布:caffe 语义分割 编辑:程序博客网 时间:2024/05/18 03:55
写在前面
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) { // During initial setup, plug in the details fragment. 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
。
@Overridepublic 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
/** * Adds an action to the queue of pending actions. * * @param action the action to add * @param allowStateLoss whether to allow loss of state information * @throws IllegalStateException if the activity has been destroyed */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(); }};/*** 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;}
这是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方法。```javawhile (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;}<div class="se-preview-section-delimiter"></div>
上面是run的部分代码,到这里我们终于看到了希望见到的Op
命令!!在run方法中,while会遍历并执行Op链表中的所有命令,执行命令的过程最终还是通过mManager来执行,拿addFragment来示例,
public void addFragment(Fragment fragment, boolean moveToStateNow) { ... if (moveToStateNow) { moveToState(fragment); }}<div class="se-preview-section-delimiter"></div>
从上面的代码看到真正控制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: ... // onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) return 的View将在这里被指向 f.mView = f.performCreateView(f.getLayoutInflater(f.mSavedFragmentState), container, f.mSavedFragmentState); //onActivityCreated(savedInstanceState); 在这里被调用 f.performActivityCreated(f.mSavedFragmentState); // Fragment的View将在这被加载进Activity中 container.addView(f.mView); ... case Fragment.ACTIVITY_CREATED: case Fragment.STOPPED: ... // onStart() 在这里被调用 f.performStart(); ... case Fragment.STARTED: ... // onResume() 在这里被调用 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); // onPause()在这被调用 f.performPause(); f.mResumed = false; } case Fragment.STARTED: if (newState < Fragment.STARTED) { if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f); // onStop()在这被调用 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) { // onDestroy 在这被回调 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实在太复杂了,一旦出现奇怪的问题,根本找不到哪个地方出的错,说多了都是泪!!!。
- 从commit一步步带你走向fragment的生命周期
- Fragment 中调用findFragmentById 报null,从Fragment的生命周期带你理解
- 从今天起,一步步的走向专业
- 时间的长河带你走向未来
- 从生命周期带你看android
- AndFix带你一步步集成,从放弃到成功
- 从源码来看Fragment的生命周期(一)
- 从源码来看Fragment的生命周期(二)
- 带你走向职场成功的4条经验
- 从官方api理解fragment,以及fragment的生命周期
- 详解fragment(一):从源代码看fragment的生命周期
- 带你一步步走入Paxos的世界 -- 序列1
- 带你一步步走入Paxos的世界 -- 序列2
- 带你一步步实现带有多彩阴影的ImageView
- 带你一步步走入Paxos的世界 -- 序列1
- 【Object C】从Java 一步步走向Object C
- 让你从小白一步步走向iOS开发巅峰!!!!
- vi 常用命令---带你走向IT巅峰
- Android 仿当乐游戏详情页面(三)
- JavaScript实现局部打印
- C运行时库(C Run-time Library)详解
- 八大排序算法的Python实现
- Spring声明式事务注解@Transactional
- 从commit一步步带你走向fragment的生命周期
- 驱动程序Makefile 的解释
- Android重启应用程序代码
- curl get post请求
- POJ 2407 Relatives (欧拉函数)
- Android dimens.xml中保存不带单位的数值
- 第四篇.android中include<>和import相关
- Linux入门笔记——文件操作命令1
- pojdijkstra+邻接表