美文网首页
2020-03-11-Android Fragment原理

2020-03-11-Android Fragment原理

作者: 耿望 | 来源:发表于2020-03-11 15:42 被阅读0次

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

    相关类

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


    Fragment原理 (3).jpg

    Fragment的添加

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

        <fragment
            android:name="world.one.com.newworld.fragment.NewFragment"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    

    如果在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

        <FrameLayout
            android:id="@+id/fragment_container"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    
    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原理

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