从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实在太复杂了,一旦出现奇怪的问题,根本找不到哪个地方出的错,说多了都是泪!!!

0 0
原创粉丝点击