美文网首页Android面试相关Android TechAndroid源码解析
从源码角度剖析Fragment核心知识点

从源码角度剖析Fragment核心知识点

作者: 呆萌狗和求疵喵 | 来源:发表于2016-03-10 23:47 被阅读2883次

    自动Android在3.0版本中退出Fragment以来,fragment在我们日常的开发中无处不在,他使我们的在开发android时能更好的做到view的解耦。关于Fragment的用法,相信大家已经用的滚瓜烂熟了,各种FragmentTransaction的操作,都信手拈来。今天我们要从源码的角度去剖析fragment内部实现的原理,我相信只有了解了内部实现原理,我们在碰到fragment的issue的时候才知道如何去解决。

    我们今天要分析的是support v4包中的Fragment,相信绝大部分人都是用的兼容包中的fragment。

    先上一张图:

    Fragment有关类结构图

    这张图描述的是与fragment相关的类的UML图,基本描述了这些类之间的关系。
    Fragment理论上可以被任何对象持有,然后管理其生命周期,但是绝大部分时候,我们都是在activity里面使用它,我们可以从Activity出发,理清楚这些类之间的关系。

    FragmentActivity

    兼容包中,支持fragment的activity叫做FragmentActivity, 我们常用的AppCompatActivity也是继承自它,

    在FragmentActivity中有一个重要的成员变量mFragments,它的类型是FragmentController

    final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
    

    正是这个mFragments的存在使得在FragmentActivity中进行fragment操作成为可能,我们可以看到在FragmentActivity中进行的任何fragment的操作都得经FragmentController之手

     @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        mFragments.attachHost(null /*parent*/);
    
        super.onCreate(savedInstanceState);
    
        //...
        mFragments.restoreAllState(p, nc != null ? nc.fragments : null);
    
        //...
        mFragments.dispatchCreate();
    }
    
    public FragmentManager getSupportFragmentManager() {
            return mFragments.getSupportFragmentManager();
    }
    

    因此,我们进到FragmentController类中去一探究竟。打开FragmentController,发现其函数实现非常简单。基本上你可以认为FragmentController是FragmentManager的代理,Activity需要的FragmentController的函数,最后调用的都是FragmentHostCallback中的FragmentManager的方法。

    FragmentHostCallback,我们可以认为他是用来管理Fragment生命周期,同时作为代理提供fragment需要与外界打交道时的函数实现的比如从fragment中启动新的Activity, 请求权限等等。其Fragment生命周期的的管理由FragmentManager负责,其余部分代理功能由其自己负责。

    我们打开FragmentHostCallback的源代码,可以看到其头顶注释中写着:fragment可以被任何对象持有,要使一个对象具有持有和管理fragment生命周期的能力,我们只需要实现FragmentHostCallback中的函数。显然,我们最常见的FragmentActivity肯定实现了FragmentHostCallback,我们跳转到FragmentActivity,看到其内部有一个非静态内部类HostCallbacks, 正式这个内部类的存在,使得FragmentActivity具有了持有和管理Fragment的能力,Fragment与外部交互的功能都由FragmentActivity实现了。至于FragmentController,前面已经说到,只是作为中间桥梁的作用。

    Fragment

    前面讲了这么多,我们还没有开始介绍今天的主角Fragment, 接下来我们就来揭开其稍许神秘的面纱。

    Fragment = Attr + View + + State, 此处等待UML图

    Attr

    Attr指得是Fragment的一些固有属性,不会随着Fragment的生命周期发生变化的,比如

    Bundle mArguments;  //构造参数
    boolean mFromLayout; //是否从layout文件中创建
    ...
    

    View

    View是Fragment创建出来并显示给用户的界面的view,如果Fragment被持有,会被添加到Activity某一块layout中去

    // The parent container of the fragment after dynamically added to UI.
    ViewGroup mContainer;
    
    // The View generated for this fragment.
    View mView;
    
    // The real inner view that will save/restore state.
    View mInnerView;
    

    State

    State指的是Fragment在生命周期变迁中中会发生改变的状态

    int mState = INITIALIZING; //生命周期状态
    boolean mAdded; //是否被添加
    boolean mRemoving; //是否被移除
    boolean mHidden;// 是否被隐藏
    boolean mDetached; //是否已经分离
    ...
    

    其中Fragment的成员变量mState,直接映射了Fragment的生命周期状态变迁,其取值状态在以下常量中:

    static final int INITIALIZING = 0;     // Not yet created.
    static final int CREATED = 1;          // Created.
    static final int ACTIVITY_CREATED = 2; // The activity has finished its creation.
    static final int STOPPED = 3;          // Fully created, not started.
    static final int STARTED = 4;          // Created and started, not resumed.
    static final int RESUMED = 5;          // Created started and resumed.
    

    我们在开中经常重写的onCreate, onResume, onStop方法都发生mState的变迁过程中。 接下来,我们就来看看Fragment的生命周期是如何变化的。

    Fragment事务操作:BackStackRecord

    我们在开发中,每次要操作那个Fragment的添加,删除,隐藏,显示等,都需要使用FragmentTransaction,比如添加一个Fragment:

    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
    transaction.add(R.id.contaniner, testFragment);
    transaction.commit();
    

    FragmentTransaction实际上是一个抽象类,里面定义了一些关于Fragment操作的函数接口,

    public abstract class FragmentTransaction {
    
        add
    
        replace
    
        remove
    
        hide 
    
        show
    
        detach
    
        attach
    
        addToBackStack
    
        ...
    
    }
    

    从FragmentManger.beginTransaction真正返回的确是一个BackStackRecord类, 其实现了FragmentTransaction所定义的接口。

    接下来,我们以添加为例,看看BackStackRecord是如何完成Add这个transaction的。

    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);
    }   
    

    BackStackRecord中实现的四个add函数,最后都调用了doAddOp,我们注意到已经添加过的Fragment其tag和被添加到的containerId是不能更改的,否则会抛异常。 函数最后new了一个Op,Op是啥?Op其实就是Operation的简称,有经验的读者应该知道在一次FragmentTransaction中实际上可以进行多次add,remove之类的操作。每次操作,都会生成一个新的Op对象,在transaction commit操作时,会将这些Operation全部执行掉

    我们来看看Op类对象里都有些啥

    static final class Op {
        Op next;
        Op prev;
        int cmd;
        Fragment fragment;
        int enterAnim;
        int exitAnim;
        int popEnterAnim;
        int popExitAnim;
        ArrayList<Fragment> removed;
    }
    

    从next, prev 两个指针可以看出,Op是作为一个链表的节点而存在的,因此FragmentTransaction肯定是在一个链表中存储了一次事务中的所有需要执行的操作。

    cmd定义了Operation操作的类型

    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;
    

    fragment指定了这次Operation要操纵哪一个Fragment
    此外Op类还包含了转场动画和一个操作会移除的Fragment集合。

    doAddOp函数最后执行了

    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++;
    }   
    

    addOp执行了一个典型的添加节点到链表末尾的数据结构操作,并将transaction的动画赋给op,最后讲mNumOp加一,mNumOp代表了这次transaction中包含的操作个数。如果为0,则isEmpty返回true.

    等到操作都添加好了之后,就差commit了。

    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;
    }
    

    如果设置了addToBackStack,就会执行添加到返回栈的操作,这个我们后面会专门讲,mManager.enqueueAction 顾名思义就是讲当前的transaction操做入队列,因此在FragmentManager肯定会维护一个队列来存储当前尚未执行的transaction.

    public void enqueueAction(Runnable action, boolean allowStateLoss) {
        ...
        synchronized (this) {
            ...
        
            mPendingActions.add(action);
            if (mPendingActions.size() == 1) {
                mHost.getHandler().removeCallbacks(mExecCommit);
                mHost.getHandler().post(mExecCommit);
            }
        }
    }
    

    enqueueAction首先会将action,添加到一个叫mPendingAction的列表中去,这个列表中存储着一堆Runnable对象,代表着还未执行的事务,稍等一会儿,我们刚刚说到的是BackStackRecord类对吧,怎么可以添加到Runnable列表中呢,原来BackStackRecod本身还是实现了Runnabled接口,是一个可以执行的对象。添加完毕之后,会调用FragmentHostCallback中提供的getHandler方法,获取到Handler方法,然后向主线程的MessageQueue中发送一个mExecCommit可执行对象

     Runnable mExecCommit = new Runnable() {
            @Override
            public void run() {
                execPendingActions();
            }
        };
    
    public boolean execPendingActions() {
    
        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];
                }
                //讲mPendingAction中的对象转移到mTmpActions
                mPendingActions.toArray(mTmpActions);
                mPendingActions.clear();
                mHost.getHandler().removeCallbacks(mExecCommit);
            }
            
            //遍历mTmpActions,执行run
            mExecutingActions = true;
            for (int i=0; i<numActions; i++) {
                mTmpActions[i].run();
                mTmpActions[i] = null;
            }
            mExecutingActions = false;
            didSomething = true;
        }
        
        ...
        return didSomething;
    }
    

    因此绕来绕去,最后调用的BackStackRecord本身的run方法,这也符合了commit方法名字本身的定义,transaction只是被批量提交到了主线程的任务队列里,并不是马上执行,等待主线程的looper去安排这些任务的执行。

    那好,我们现在回家吧,去看看BackStackRecord。

    public void run() {
        ...
        
        Op op = mHead;
        while (op != null) {
            int enterAnim = state != null ? 0 : op.enterAnim;
            int exitAnim = state != null ? 0 : op.exitAnim;
            switch (op.cmd) {
                case OP_ADD: {
                    Fragment f = op.fragment;
                    f.mNextAnim = enterAnim;
                    mManager.addFragment(f, false);
                } break;
                case OP_REPLACE: {
                      ... 
                } break;
                case OP_REMOVE: {
                    Fragment f = op.fragment;
                    f.mNextAnim = exitAnim;
                    mManager.removeFragment(f, transition, transitionStyle);
                } break;
                case OP_HIDE: {
                    ...
                } break;
                ...
                //更多case
            }
    
            op = op.next;
        }
        //将active状态的fragment全部执行状态变迁检查
        mManager.moveToState(mManager.mCurState, transition, transitionStyle, true);
    
        if (mAddToBackStack) {
            mManager.addBackStackState(this);
        }
    }
    

    run方法内部根据不同的cmd走了很不不同的分支,每个分支内部都会对,fragment状态做改变,最后调用moveToState将fragment的生命周期状态mState进行变迁。

    当然我们注意到hide和show,它们内部实际上不会调用调用moveToState, 因为hideFragment实际上就做了三件事请,

    • 设置mHidden 为true
    • fragment.mView.setVisibility(View.GONE); 隐藏fragment的view
    • fragment.onHiddenChanged(true); 调用onHiddenChange

    showFragment也是类似,只不过行为正好相反。

    public void addFragment(Fragment fragment, boolean moveToStateNow) {
        ...
        makeActive(fragment);
        if (!fragment.mDetached) {
            ...
            mAdded.add(fragment);
            fragment.mAdded = true;
            fragment.mRemoving = false;
            if (fragment.mHasMenu && fragment.mMenuVisible) {
                mNeedMenuInvalidate = true;
            }
            if (moveToStateNow) {
                moveToState(fragment);
            }
        }
    }
    
    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);
    }
    

    addFragment会将fragment添加到 mAdded和 mActive这两个集合当中,这两个集合维护了当前activity中维护的已经添加的fragment列表和当前处于活跃状态的fragment列表,如果fragment位于mActive中,那么当activity的状态发生变化时,fragment也会跟随着发生变化。FragmentManger 如何引导fragment的状态发生变化呢?

    这一切都发生在moveToState函数当中

    Fragment状态变迁:moveToState

    Fragment状态变迁发生在用户主动发起transaction,或者fragment被add到activity之后跟随activity的生命周期变化一起发生改变。每次状态变迁最终都会走到函数moveToState,字面意思是将fragment迁移到新的状态

    void moveToState(Fragment f, int newState, int transit, int transitionStyle,
            boolean keepActive) {
        // Fragments that are not currently added will sit in the onCreate() state.
        ...
        if (f.mState < newState) {
            // For fragments that are created from a layout, when restoring from
            // state we don't want to allow them to be created until they are
            // being reloaded from the layout.
            ...
            switch (f.mState) {
                case Fragment.INITIALIZING:
                    ...
                case Fragment.CREATED:
                    if (newState > Fragment.CREATED) {
                       ...
                    }
                case Fragment.ACTIVITY_CREATED:
                case Fragment.STOPPED:
                    if (newState > Fragment.STOPPED) {
                        if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
                        f.performStart();
                    }
                case Fragment.STARTED:
                    if (newState > Fragment.STARTED) {
                        if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
                        f.performResume();
                        f.mSavedFragmentState = null;
                        f.mSavedViewState = null;
                    }
            }
        } 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();
                    }
                case Fragment.STARTED:
                    if (newState < Fragment.STARTED) {
                        if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
                        f.performStop();
                    }
                case Fragment.STOPPED:
                    if (newState < Fragment.STOPPED) {
                        if (DEBUG) Log.v(TAG, "movefrom STOPPED: " + f);
                        f.performReallyStop();
                    }
                case Fragment.ACTIVITY_CREATED:
                    if (newState < Fragment.ACTIVITY_CREATED) {
                       ...
                    }
                case Fragment.CREATED:
                    if (newState < Fragment.CREATED) {
                        ...
                    }
            }
        }
    
        ...
    }
    

    fragment的state取值,为前面提到的七中状态,其中最低值是INITIALIZING状态,代表fragment刚创建,还未被add, 最高状态值是RESUMED,代表fragment处于前台。 所以moveToState内部分两条线,状态跃升,和状态降低,里面各有一个switch判断,注意到switch里每个case都没有break,这意味着,状态可以持续变迁,比如从INITIALIZING,一直跃升到RESUMED,将每个case都走一遍,每次case语句内,都会改变state的值。

    Fragment状态变迁图

    比如我们常见的add操作,最后调用的是

    mManager.moveToState(mManager.mCurState, transition, transitionStyle, true);
    

    就是将fragment迁移到FragmentManager当前的状态,因为我们不知道用户什么时候add fragment,因此fragment被add之后,就将其状态迁移到FragmentManager当前的状态,然后跟随FragmentManager一起发生状态变迁,除非用户手动removeFragment将其从mActive列表中移除。

    public void dispatchCreate() {
        mStateSaved = false;
        moveToState(Fragment.CREATED, false);
    }
    
    public void dispatchActivityCreated() {
        mStateSaved = false;
        moveToState(Fragment.ACTIVITY_CREATED, false);
    }
    
    public void dispatchStart() {
        mStateSaved = false;
        moveToState(Fragment.STARTED, false);
    }
    
    public void dispatchResume() {
        mStateSaved = false;
        moveToState(Fragment.RESUMED, false);
    }
    
    public void dispatchPause() {
        moveToState(Fragment.STARTED, false);
    }
    
    public void dispatchStop() {
        // See saveAllState() for the explanation of this.  We do this for
        // all platform versions, to keep our behavior more consistent between
        // them.
        mStateSaved = true;
    
        moveToState(Fragment.STOPPED, false);
    }
    
    public void dispatchReallyStop() {
        moveToState(Fragment.ACTIVITY_CREATED, false);
    }
    
    public void dispatchDestroyView() {
        moveToState(Fragment.CREATED, false);
    }
    
    public void dispatchDestroy() {
        mDestroyed = true;
        execPendingActions();
        moveToState(Fragment.INITIALIZING, false);
        mHost = null;
        mContainer = null;
        mParent = null;
    }
    

    这些 dispatchxxx函数由FragmentActivity状态变化引发,然后调用moveToState将处于mActive集合中的fragment的状态全部变更一次。
    比如,当FragmentActivity pause的时候,其会通过FM
    通知framgents进行状态变迁。

    /**
     * Dispatch onPause() to fragments.
     */
    @Override
    protected void onPause() {
        super.onPause();
        mResumed = false;
        if (mHandler.hasMessages(MSG_RESUME_PENDING)) {
            mHandler.removeMessages(MSG_RESUME_PENDING);
            onResumeFragments();
        }
        mFragments.dispatchPause();
    }
    

    Fragment状态的保存

    既然Fragment持有view,以及一些状态属性,那么在Activity保存自身状态以便下次恢复的时候,就需要把fragment的状态也保存起来,这样activity被系统finish掉,然后重新创建时就能恢复上次的fragment状态。那我们首先直奔FragmentActivity的onSaveInstanceState函数:

     /**
     * Save all appropriate fragment state.
     */
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        //调用FragmentController 代理FragmentManager 保存状态
        Parcelable p = mFragments.saveAllState();
        if (p != null) {
            outState.putParcelable(FRAGMENTS_TAG, p);
        }
        //保存跟fragment启动activity等待result的状态
        if (mPendingFragmentActivityResults.size() > 0) {
            outState.putInt(NEXT_CANDIDATE_REQUEST_INDEX_TAG, mNextCandidateRequestIndex);
    
            int[] requestCodes = new int[mPendingFragmentActivityResults.size()];
            String[] fragmentWhos = new String[mPendingFragmentActivityResults.size()];
            for (int i = 0; i < mPendingFragmentActivityResults.size(); i++) {
                requestCodes[i] = mPendingFragmentActivityResults.keyAt(i);
                fragmentWhos[i] = mPendingFragmentActivityResults.valueAt(i);
            }
            outState.putIntArray(ALLOCATED_REQUEST_INDICIES_TAG, requestCodes);
            outState.putStringArray(REQUEST_FRAGMENT_WHO_TAG, fragmentWhos);
        }
    }
    

    saveAllState函数就会将FragmentManager在重新恢复fragmentstate时需要的所有信息保存起来,小伙伴们可以跟进去看,我在这里画了张图:

    FragmentManager状态的保存

    关于返回栈的保存没有详细标注,不过我们后面会再详细讲到。

    Fragment 状态的恢复

    Fragment状态的恢复其实就是保存状态的逆过程,不过,额外的工作是,我们需要根据之前保存的每个active的fragmentstate来恢复创建Fragment:

    
    public Fragment instantiate(FragmentHostCallback host, Fragment parent) {
        if (mInstance != null) {
            return mInstance;
        }
    
        final Context context = host.getContext();
        if (mArguments != null) {
            mArguments.setClassLoader(context.getClassLoader());
        }
        //利用保存的状态恢复fragment
        mInstance = Fragment.instantiate(context, mClassName, mArguments);
    
        if (mSavedFragmentState != null) {
            mSavedFragmentState.setClassLoader(context.getClassLoader());
            mInstance.mSavedFragmentState = mSavedFragmentState;
        }
        mInstance.setIndex(mIndex, parent);
        mInstance.mFromLayout = mFromLayout;
        mInstance.mRestored = true;
        mInstance.mFragmentId = mFragmentId;
        mInstance.mContainerId = mContainerId;
        mInstance.mTag = mTag;
        mInstance.mRetainInstance = mRetainInstance;
        mInstance.mDetached = mDetached;
        mInstance.mFragmentManager = host.mFragmentManager;
    
        if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
                "Instantiated fragment " + mInstance);
    
        return mInstance;
    }
    

    其中构造Fragment的代码如下:

      public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) {
        try {
            Class<?> clazz = sClassMap.get(fname);
            if (clazz == null) {
                // Class not found in the cache, see if it's real, and try to add it
                clazz = context.getClassLoader().loadClass(fname);
                sClassMap.put(fname, clazz);
            }
            Fragment f = (Fragment)clazz.newInstance();
            if (args != null) {
                args.setClassLoader(f.getClass().getClassLoader());
                f.mArguments = args;
            }
            return f;
        } catch (ClassNotFoundException e) {
            throw new InstantiationException("Unable to instantiate fragment " + fname
                    + ": make sure class name exists, is public, and has an"
                    + " empty constructor that is public", e);
        } catch (java.lang.InstantiationException e) {
            throw new InstantiationException("Unable to instantiate fragment " + fname
                    + ": make sure class name exists, is public, and has an"
                    + " empty constructor that is public", e);
        } catch (IllegalAccessException e) {
            throw new InstantiationException("Unable to instantiate fragment " + fname
                    + ": make sure class name exists, is public, and has an"
                    + " empty constructor that is public", e);
        }
    }
    

    是不是发现这段代码灰常熟悉,很多人恐怕都遇到过代码中描述的异常。因为Fragment在恢复时,是利用反射的方式去创建,首先利用类加载器去加载类,然后调用其pubic empty 构造函数去创建fragment。

    所以如果Fragment不是public的,或者fragment没有public的无参构造函数,那么你的应用肯定会碰到这些异常,只不过你在调试的时候可能无法发现,因为你的手机环境太好,构不成saveInstanceState然后恢复它们的条件。

    如果我们用Android studio 模板去创建fragment,那么他会给我们默认实现一个newInstance的静态构造函数,并把构造参数写在argument里,因为前面那张图里可以看到,argument会在保存fragment状态时保存起来的。另外不要写任何带参数的构造函数,因为这样子,默认构造函数就会被隐藏,除非你手动去实现它。 话说这个instantiate是个public方法,因此你也可以直接调用这个函数去创建fragment,只不过它使用起来不是那么方便,使用者不知道我们需要往里面传什么参数。

    MyFragment.instantiate(activity, MyFragment.getClass().getName(), args)
    
    臭名昭著的 “Can not perform this action after onSaveInstanceState”

    这里不得不提一提开发中经常会碰到的一个异常,异常抛出时的message为:“Can not perform this action after onSaveInstanceState”, 意为,FragmentTransaction不能再onSaveInstanceState后提交,为什么会抛出这样一个异常呢,因为FragmentManager认为在onSaveInstanceState 发生之后提交的transaction不能在下次Fm恢复时得到恢复,继而认为这样做是危险的,拒绝提交,除非你指定了allowStateLoss,即允许状态的丢失。

    开发者只需要用commitAllowingStateLoss,即可以成功提交这样的transaction,即使它有可能会丢失状态。

    Fragment返回栈

    使用fragmentTransaction的时候可以将其加入返回栈,这样用户就可以有机会去撤销这一动作,将其从返回栈中pop出来。

    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;
    }
    
    //transaction提交
    public void run() {
        //...
    
        //加入到FragmentManager的返回栈中
        if (mAddToBackStack) {
            mManager.addBackStackState(this);
        }
    }
    
    //FragmentManager添加返回栈
     void addBackStackState(BackStackRecord state) {
        if (mBackStack == null) {
            mBackStack = new ArrayList<BackStackRecord>();
        }
        mBackStack.add(state);
        //通知返回栈监听者们发生改变
        reportBackStackChanged();
    }
        
    

    FragmentManager中有一个叫做mBackStack的列表,保存了添加到返回栈的所有record, 当用户选择popBackStack的时候,就可以将其pop出来。

    我们常用的pop操作就是popBackStack函数,其实FM还提供了很多pop操作,它可以指定以name和id指定pop哪个record,还可以指定flag:POP_BACK_STACK_INCLUSIVE, 如果指定了这个flag就表示将name或者id相等且连续的record全部pop出来。这些操作最后都调用了函数

    boolean popBackStackState(Handler handler, String name, int id, int flags) {
        //如果返回栈不存在,立即返回
        if (mBackStack == null) {
            return false;
        }
        //如果name、id、POP_BACK_STACK_INCLUSIVE全部都没有设置,直接移除栈顶的一个record
        if (name == null && id < 0 && (flags&POP_BACK_STACK_INCLUSIVE) == 0) {
            int last = mBackStack.size()-1;
            if (last < 0) {
                return false;
            }
            final BackStackRecord bss = mBackStack.remove(last);
            SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
            SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
            bss.calculateBackFragments(firstOutFragments, lastInFragments);
            bss.popFromBackStack(true, null, firstOutFragments, lastInFragments);
            reportBackStackChanged();
        } else {
            int index = -1;
            if (name != null || id >= 0) {
                //从栈顶开始寻找一个name或者id匹配的record,找到即跳出循环
                index = mBackStack.size()-1;
                while (index >= 0) {
                    BackStackRecord bss = mBackStack.get(index);
                    if (name != null && name.equals(bss.getName())) {
                        break;
                    }
                    if (id >= 0 && id == bss.mIndex) {
                        break;
                    }
                    index--;
                }
                //如果没找到,函数返回
                if (index < 0) {
                    return false;
                }
                //如果设置了POP_BACK_STACK_INCLUSIVE,则向栈底方向寻找连续匹配的record
                if ((flags&POP_BACK_STACK_INCLUSIVE) != 0) {
                    index--;
                    // Consume all following entries that match.
                    while (index >= 0) {
                        BackStackRecord bss = mBackStack.get(index);
                        if ((name != null && name.equals(bss.getName()))
                                || (id >= 0 && id == bss.mIndex)) {
                            index--;
                            continue;
                        }
                        break;
                    }
                }
            }
            if (index == mBackStack.size()-1) {
                return false;
            }
            //弹出从栈顶到连续相同匹配的record记录位置的所有record,加入集合states中
            final ArrayList<BackStackRecord> states
                    = new ArrayList<BackStackRecord>();
            for (int i=mBackStack.size()-1; i>index; i--) {
                states.add(mBackStack.remove(i));
            }
         
            final int LAST = states.size()-1;
            SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
            SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
            //计算在连续record中,最先remove的fragment集合 和 最后进入的fragment集合
            for (int i=0; i<=LAST; i++) {
                states.get(i).calculateBackFragments(firstOutFragments, lastInFragments);
            }
            BackStackRecord.TransitionState state = null;
            //对这些record列表执行逆操作
            for (int i=0; i<=LAST; i++) {
                if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i));
                state = states.get(i).popFromBackStack(i == LAST, state,
                        firstOutFragments, lastInFragments);
            }
            //通知返回栈监听器更新
            reportBackStackChanged();
        }
        return true;
    }
    

    其中比较复杂的两步,一步是calculateBackFragments, 还有一个是popFromBackStack,我们先看calculateBackFragments:

    为什么需要计算calculateBackFragments? 其实是因为涉及到动画的方面,在一次在一次transaction中可能会移除和添加多次fragments,这样动画的编排就必须按顺序来,这次分析中,我们就不对动画做过多分析了,因为这是相对来说次要点的知识点。所以我们集中关心BackStackRecord 的popFromBackStack函数

    public TransitionState popFromBackStack(boolean doStateMove, TransitionState state,
                SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments) {
        
        //...动画相关
    
    
        int transitionStyle = state != null ? 0 : mTransitionStyle;
        int transition = state != null ? 0 : mTransition;
        Op op = mTail;
        while (op != null) {
            int popEnterAnim = state != null ? 0 : op.popEnterAnim;
            int popExitAnim= state != null ? 0 : op.popExitAnim;
            switch (op.cmd) {
                case OP_ADD: {
                    Fragment f = op.fragment;
                    f.mNextAnim = popExitAnim;
                    mManager.removeFragment(f,
                            FragmentManagerImpl.reverseTransit(transition), transitionStyle);
                } break;
                case OP_REPLACE: {
                    Fragment f = op.fragment;
                    if (f != null) {
                        f.mNextAnim = popExitAnim;
                        mManager.removeFragment(f,
                                FragmentManagerImpl.reverseTransit(transition), transitionStyle);
                    }
                    if (op.removed != null) {
                        for (int i=0; i<op.removed.size(); i++) {
                            Fragment old = op.removed.get(i);
                            old.mNextAnim = popEnterAnim;
                            mManager.addFragment(old, false);
                        }
                    }
                } break;
                case OP_REMOVE: {
                    Fragment f = op.fragment;
                    f.mNextAnim = popEnterAnim;
                    mManager.addFragment(f, false);
                } break;
                case OP_HIDE: {
                    Fragment f = op.fragment;
                    f.mNextAnim = popEnterAnim;
                    mManager.showFragment(f,
                            FragmentManagerImpl.reverseTransit(transition), transitionStyle);
                } break;
                case OP_SHOW: {
                    Fragment f = op.fragment;
                    f.mNextAnim = popExitAnim;
                    mManager.hideFragment(f,
                            FragmentManagerImpl.reverseTransit(transition), transitionStyle);
                } break;
                case OP_DETACH: {
                    Fragment f = op.fragment;
                    f.mNextAnim = popEnterAnim;
                    mManager.attachFragment(f,
                            FragmentManagerImpl.reverseTransit(transition), transitionStyle);
                } break;
                case OP_ATTACH: {
                    Fragment f = op.fragment;
                    f.mNextAnim = popEnterAnim;
                    mManager.detachFragment(f,
                            FragmentManagerImpl.reverseTransit(transition), transitionStyle);
                } break;
                default: {
                    throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
                }
            }
    
            op = op.prev;
        }
    
        if (doStateMove) {
            mManager.moveToState(mManager.mCurState,
                    FragmentManagerImpl.reverseTransit(transition), transitionStyle, true);
            state = null;
        }
    
        if (mIndex >= 0) {
            mManager.freeBackStackIndex(mIndex);
            mIndex = -1;
        }
        return state;
    }
    

    基本上从多个switch-case分支语句里就可以看出pop执行的就是commit的反操作,因为因为transaction都是成对存在的

    • add <-> remove
    • attach <-> detach
    • show <-> hide
    • replace( remove x n + add x m) <-> replace ( remove x m + add x n)

    返回栈的状态的保存

    返回栈状态的保存,相当于要把整个BackStackRecord的列表保存下来,以便下次恢复,前面提到BackStackRecord,可以跟 BackStackState这个parcelable映射起来。基本上BackStackRecord都是可以parcel的类型,出了Op链表,Op链表映射为BackStackState的int[] mOps;

    看完了返回栈,基本上有关Fragment的核心知识点都了解的差不多了,当然我们也跳过了一些知识点,比如有关动画的部分、ChildFragmentManager等。但基本上Fragment在使用到遇到问题的部分,都可以从上面的分析中找到原因。

    相关文章

      网友评论

      • HWilliamgo:写的真他妈的好
      • ce3d33f29fd3:非常棒,把fragment实现原理和步骤讲的很清晰
      • 我不是番茄请叫我西红柿:有错别字,是推出而不是退出...
      • d7f21c6da5e2:非常不错
      • 57a4a9eb5580:写的很牛逼,但是博主更新下文章吧,sdk25以后,Fragment的一些操作发生了变化,在看源码的时候发现很多地方和文章中不一致了。
      • 神天圣地:感谢分享,很不错, :+1:
      • 黄光华:感谢分享~
      • Jeongho:今天看到这部分源码,绕得好迷糊。BackStackRecord源码还没研究。。得消化消化,刚好搜到博主的狂拽酷炫吊炸天的源码分析,明天再捋一捋。必须打赏~!
      • fe9fe0de7b28:牛逼,很清楚
        呆萌狗和求疵喵:@_wait_long 谢谢 :smile:

      本文标题:从源码角度剖析Fragment核心知识点

      本文链接:https://www.haomeiwen.com/subject/muuokttx.html