2020-03-11-Android Fragment原理

最近刚好遇到了一个问题,新需求快稳省测试发现,应用Home键回到桌面后,电流没有回归,抓了一份systrace,发现ui进程还在跟SurfaceFlinger通信,有animation动画和binder通信,应该是一个VSync信号就有一次通信,花了一天时间才找出来,是同事在Fragment的onViewCreated方法里,通过listParent去加载布局,布局里有个动画的控件,而这个view是没有正常onPause的,所以后台一直在加载动画。
踩坑之后,决定研究一下Fragment的原理。

相关类

主要有四个类,FragmentManager是个抽象类,实现在FragmentManagerImpl;
FragmentTransaction也是个抽象类,实现类是BackStackRecord。


Fragment原理 (3).jpg

Fragment的添加

第一种方法是直接将fragment添加到布局中。

    

如果在NewFragment的onCreateView中打印堆栈,会看到

    ----world.one.com.newworld.fragment.NewFragment.onCreateView(NewFragment.java:19)
    ----android.support.v4.app.Fragment.performCreateView(Fragment.java:2439)
    ----android.support.v4.app.FragmentManagerImpl.ensureInflatedFragmentView(FragmentManager.java:1689)
    ----android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1431)
    ----android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1684)
    ----android.support.v4.app.FragmentManagerImpl.addFragment(FragmentManager.java:1930)
    ----android.support.v4.app.FragmentManagerImpl.onCreateView(FragmentManager.java:3745)
    ----android.support.v4.app.FragmentController.onCreateView(FragmentController.java:120)
    ----android.support.v4.app.FragmentActivity.dispatchFragmentsOnCreateView(FragmentActivity.java:405)
    ----android.support.v4.app.FragmentActivity.onCreateView(FragmentActivity.java:387)

第二种方法是动态替换,将布局中的某个view替换成fragment

    
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, new NewFragment()).commit();

虽然只有一行代码,但是调用的过程是很复杂的。

    ----world.one.com.newworld.fragment.NewFragment.onCreateView(NewFragment.java:19)
    ----android.support.v4.app.Fragment.performCreateView(Fragment.java:2439)
    ----android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1460)
    ----android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1784)
    ----android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1852)
    ----android.support.v4.app.BackStackRecord.executeOps(BackStackRecord.java:802)
    ----android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2625)
    ----android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2411)
    ----android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2366)
    ----android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2273)
    ----android.support.v4.app.FragmentManagerImpl.dispatchStateChange(FragmentManager.java:3273)
    ----android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:3229)
    ----android.support.v4.app.FragmentController.dispatchActivityCreated(FragmentController.java:201)
    ----android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:620)
    ----android.support.v7.app.AppCompatActivity.onStart(AppCompatActivity.java:178)
Fragment.jpg

先看下add方法添加一个Fragment到布局的过程。实际上是通过doAddOp方法实现的,需要注意的是注释1处传入的opcmd是OP_ADD,需要跟replace方法区分。

    public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
        doAddOp(containerViewId, fragment, tag, OP_ADD);//1
        return this;
    }

add方法通过doAddOp将fragment封装成一个Op对象。
1.将fragmentManager指定为当前manager;
2.如果tag存在,将fragment的tag指定为传入的tag;
3.将fragment的ContainerId和FragmentId都指定为传入的containerViewId;
4.将fragment和opcmd封装成一个Op对象,执行addOp。

    private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
        if (mManager.getTargetSdk() > Build.VERSION_CODES.N_MR1) {
            final Class fragmentClass = fragment.getClass();
            final int modifiers = fragmentClass.getModifiers();
            if ((fragmentClass.isAnonymousClass() || !Modifier.isPublic(modifiers)
                    || (fragmentClass.isMemberClass() && !Modifier.isStatic(modifiers)))) {
                throw new IllegalStateException("Fragment " + fragmentClass.getCanonicalName()
                        + " must be a public static class to be  properly recreated from"
                        + " instance state.");
            }
        }
        fragment.mFragmentManager = mManager;//1

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

        if (containerViewId != 0) {
            if (containerViewId == View.NO_ID) {
                throw new IllegalArgumentException("Can't add fragment "
                        + fragment + " with tag " + tag + " to container view with no id");
            }
            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;//3
        }

        addOp(new Op(opcmd, fragment));//4
    }

BackStackRecord内部维持了一个ArrayList列表mOps,用来保存fragment封装后的Op对象。

    void addOp(Op op) {
        mOps.add(op);
        op.enterAnim = mEnterAnim;
        op.exitAnim = mExitAnim;
        op.popEnterAnim = mPopEnterAnim;
        op.popExitAnim = mPopExitAnim;
    }

replace方法跟add类似,都是通过doAddOp实现的,只是参数不同。注释1处传入的opcmd是OP_REPLACE。

    public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
        if (containerViewId == 0) {
            throw new IllegalArgumentException("Must use non-zero containerViewId");
        }

        doAddOp(containerViewId, fragment, tag, OP_REPLACE);//1
        return this;
    }

其他操作还有remove,hide,show等,实际上都是通过addOp方法往ArrayList中插入一个Op对象,只是它们的opcmd参数不同。

    public FragmentTransaction remove(Fragment fragment) {
        addOp(new Op(OP_REMOVE, fragment));

        return this;
    }
    public FragmentTransaction hide(Fragment fragment) {
        addOp(new Op(OP_HIDE, fragment));

        return this;
    }
    public FragmentTransaction show(Fragment fragment) {
        addOp(new Op(OP_SHOW, fragment));

        return this;
    }
    public FragmentTransaction detach(Fragment fragment) {
        addOp(new Op(OP_DETACH, fragment));

        return this;
    }
    public FragmentTransaction attach(Fragment fragment) {
        addOp(new Op(OP_ATTACH, fragment));

        return this;
    }

不管是什么操作,add,replace或remove,最后一步的操作是commit

    public int commit() {
        return commitInternal(false);
    }
    int commitInternal(boolean allowStateLoss) {
        if (mCommitted) {
            throw new IllegalStateException("commit already called");
        }
        if (FragmentManagerImpl.DEBUG) {
            Log.v(TAG, "Commit: " + this);
            LogWriter logw = new LogWriter(Log.VERBOSE, TAG);
            PrintWriter pw = new FastPrintWriter(logw, false, 1024);
            dump("  ", null, pw, null);
            pw.flush();
        }
        mCommitted = true;
        if (mAddToBackStack) {
            mIndex = mManager.allocBackStackIndex(this);
        } else {
            mIndex = -1;
        }
        mManager.enqueueAction(this, allowStateLoss);
        return mIndex;
    }
    public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
        if (!allowStateLoss) {
            checkStateLoss();
        }
        synchronized (this) {
            if (mDestroyed || mHost == null) {
                if (allowStateLoss) {
                    // This FragmentManager isn't attached, so drop the entire transaction.
                    return;
                }
                throw new IllegalStateException("Activity has been destroyed");
            }
            if (mPendingActions == null) {
                mPendingActions = new ArrayList<>();
            }
            mPendingActions.add(action);
            scheduleCommit();
        }
    }

最终实现是在executeOps,这里遍历mOps数组,根据cmd对每个数组执行操作。

    void executeOps() {
        final int numOps = mOps.size();
        for (int opNum = 0; opNum < numOps; opNum++) {
            final Op op = mOps.get(opNum);
            final Fragment f = op.fragment;
            if (f != null) {
                f.setNextTransition(mTransition, mTransitionStyle);
            }
            switch (op.cmd) {
                case OP_ADD:
                    f.setNextAnim(op.enterAnim);
                    mManager.addFragment(f, false);
                    break;
                case OP_REMOVE:
                    f.setNextAnim(op.exitAnim);
                    mManager.removeFragment(f);
                    break;
                case OP_HIDE:
                    f.setNextAnim(op.exitAnim);
                    mManager.hideFragment(f);
                    break;
                case OP_SHOW:
                    f.setNextAnim(op.enterAnim);
                    mManager.showFragment(f);
                    break;
                case OP_DETACH:
                    f.setNextAnim(op.exitAnim);
                    mManager.detachFragment(f);
                    break;
                case OP_ATTACH:
                    f.setNextAnim(op.enterAnim);
                    mManager.attachFragment(f);
                    break;
                case OP_SET_PRIMARY_NAV:
                    mManager.setPrimaryNavigationFragment(f);
                    break;
                case OP_UNSET_PRIMARY_NAV:
                    mManager.setPrimaryNavigationFragment(null);
                    break;
                default:
                    throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
            }
            if (!mReorderingAllowed && op.cmd != OP_ADD && f != null) {
                mManager.moveFragmentToExpectedState(f);
            }
        }
        if (!mReorderingAllowed) {
            // Added fragments are added at the end to comply with prior behavior.
            mManager.moveToState(mManager.mCurState, true);
        }
    }

onCreateView和onViewCreated的区别

首先,它们在时序上是有先后的,onCreateView先执行,然后是onViewCreated。

参考

https://blog.csdn.net/u011240877/article/details/78132990
https://xiazdong.github.io/2017/06/15/android-fragment/

你可能感兴趣的:(2020-03-11-Android Fragment原理)