美文网首页
Android-Fragment的生命周期、FragmentSt

Android-Fragment的生命周期、FragmentSt

作者: zzq_nene | 来源:发表于2020-07-26 20:21 被阅读0次
    image.png

    一、FragmentTransaction的add、remove、show、hide、attach、detach、replace、addToStack

    (1)add和remove

    如果使用add和remove的时候,不使用到addToStack方法将fragment添加到回退栈,那么在remove的时候会完全销毁fragment

    (2)show和hide

    只是针对fragment做了一个显示和隐藏,并不会销毁fragment

    (3)attach和detach

    detach的时候,fragment会销毁视图,但是不会完全销毁。

    (4)replace

    replace是remove+add的操作。执行remove操作,那么fragment就会被销毁,直到detach,add一个fragment,就会创建一个新的fragment

    (5)addToStack

    就是将fragment添加到回退栈中,如果在调用replace的时候又调用了addToStack的话,那么在replace中执行remove操作的是,并不会完全销毁fragment,只是将fragment的视图结束。

    二、Fragment.onHiddenChanged

    用FragmentTransaction来控制fragment的hide和show时,那么这个方法就会被调用。每当你对某个Fragment使用hide或者是show的时候,那么这个Fragment就会自动调用这个方法。

    三、Fragment.setUserVisibleHint

    在调用mCurTransaction.commitNowAllowingStateLoss();的时候会激活调用该方法,因为此时会调用FragmentPagerAdapter.setPrimaryItem方法,在setPrimaryItem方法中会调用Fragment.setUserVisibleHint方法
    不过fragment的setUserVisibleHint方法已经是过时的方法,官方API推荐使用FragmentTransaction#setMaxLifecycle(Fragment, Lifecycle.State)替换

    getSupportFragmentManager().beginTransaction().setMaxLifecycle()
    

    setUserVisibleHint方法的触发
    因为ViewPager是控制Fragment的可见与不可见的,所以先看ViewPager的setCurrentItem

    1.ViewPager#setCurrentItem

        public void setCurrentItem(int item) {
            mPopulatePending = false;
            setCurrentItemInternal(item, !mFirstLayout, false);
        }
    

    setCurrentItem最终调用了setCurrentItemInternal方法,且是参数最多的一个

    2.ViewPager#setCurrentItemInternal

        void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
            if (mAdapter == null || mAdapter.getCount() <= 0) {
                setScrollingCacheEnabled(false);
                return;
            }
            if (!always && mCurItem == item && mItems.size() != 0) {
                setScrollingCacheEnabled(false);
                return;
            }
    
            if (item < 0) {
                item = 0;
            } else if (item >= mAdapter.getCount()) {
                item = mAdapter.getCount() - 1;
            }
            final int pageLimit = mOffscreenPageLimit;
            if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) {
                // We are doing a jump by more than one page.  To avoid
                // glitches, we want to keep all current pages in the view
                // until the scroll ends.
                for (int i = 0; i < mItems.size(); i++) {
                    mItems.get(i).scrolling = true;
                }
            }
            final boolean dispatchSelected = mCurItem != item;
    
            if (mFirstLayout) {
                // We don't have any idea how big we are yet and shouldn't have any pages either.
                // Just set things up and let the pending layout handle things.
                mCurItem = item;
                if (dispatchSelected) {
                    dispatchOnPageSelected(item);
                }
                requestLayout();
            } else {
                populate(item);
                scrollToItem(item, smoothScroll, velocity, dispatchSelected);
            }
        }
    

    当不是第一次mFirstLayout参数在ViewPager的onLayout方法结束之后就会置为false

    3.ViewPager#populate(int newCurrentItem)

    在ViewPager的populate方法中,会调用了
    mAdapter.setPrimaryItem(this, mCurItem, curItem.object);
    而curItem初始的时候为空,赋值的部分是在populate中调用了下面的代码

            for (curIndex = 0; curIndex < mItems.size(); curIndex++) {
                final ItemInfo ii = mItems.get(curIndex);
                if (ii.position >= mCurItem) {
                    if (ii.position == mCurItem) curItem = ii;
                    break;
                }
            }
    
            if (curItem == null && N > 0) {
                curItem = addNewItem(mCurItem, curIndex);
            }
    

    在分析ViewPager的setAdapter的时候,最初的时候mItems其实都是空的,第一个数据其实就是通过调用对应的adapter的instantiateItem获取到
    mAdapter.setPrimaryItem(this, mCurItem, curItem.object);中,curItem的object其实就是Fragment,那么接着分析

    4.ViewPager#addNewItem

        ItemInfo addNewItem(int position, int index) {
            ItemInfo ii = new ItemInfo();
            ii.position = position;
     // 从第五点可以知道这里的object其实就是Fragment对象,如果是ViewPager+Fragment
            ii.object = mAdapter.instantiateItem(this, position);
            ii.widthFactor = mAdapter.getPageWidth(position);
            if (index < 0 || index >= mItems.size()) {
                mItems.add(ii);
            } else {
                mItems.add(index, ii);
            }
            return ii;
        }
    

    5.FragmentPagerAdapter#instantiateItem

    因为是ViewPager+Fragment的做法,所以ViewPager中的adapter其实就是FragmentPagerAdapter,从FragmentPagerAdapter的instantiateItem方法可以看出,这里返回的是一个Fragment对象

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            if (mCurTransaction == null) {
                mCurTransaction = mFragmentManager.beginTransaction();
            }
    
            final long itemId = getItemId(position);
    
            // 是否已经有这个Fragment,如果有,则直接从FragmentManager中取出
            String name = makeFragmentName(container.getId(), itemId);
            Fragment fragment = mFragmentManager.findFragmentByTag(name);
            if (fragment != null) {
                if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
                mCurTransaction.attach(fragment);
            } else {
         // 如果还没有这个Fragment,则通过getItem获取到这个Fragment对象
                fragment = getItem(position);
                if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
                mCurTransaction.add(container.getId(), fragment,
                        makeFragmentName(container.getId(), itemId));
            }
            if (fragment != mCurrentPrimaryItem) {
                fragment.setMenuVisibility(false);
                fragment.setUserVisibleHint(false);
            }
    
            return fragment;
        }
    

    6.FragmentPagerAdapter#setPrimaryItem

    setPrimaryItem方法其实就是具体去调用对应的Fragment的显示隐藏的回调的方法,在这里会先判断被选择的item是否是与当前的item一致,如果被选择的fragment与当前的一致,则什么都不做,如果不一致,则将当前的Fragment的显示隐藏的回调置为false,将新的被选择的Fragment置为true,并且更新当前Fragment对象缓存

        @Override
        public void setPrimaryItem(ViewGroup container, int position, Object object) {
            Fragment fragment = (Fragment)object;
            if (fragment != mCurrentPrimaryItem) {
                if (mCurrentPrimaryItem != null) {
                    mCurrentPrimaryItem.setMenuVisibility(false);
                    mCurrentPrimaryItem.setUserVisibleHint(false);
                }
                if (fragment != null) {
                    fragment.setMenuVisibility(true);
                    fragment.setUserVisibleHint(true);
                }
                mCurrentPrimaryItem = fragment;
            }
        }
    

    四、FragmentTransaction.setMaxLifecycle

    1.Lifecycle.State的各种状态

    使用FragmentTransaction.setMaxLifecycle设置Fragment的最大生命周期,代表Fragment只能执行到设置的生命周期,而不能继续往下一个生命周期执行。比如:

            fragment = TestLifecycleFragment()
            val fragmentTransaction = supportFragmentManager.beginTransaction()
            fragmentTransaction.add(R.id.ll_fragment, fragment)
            fragmentTransaction.setMaxLifecycle(fragment, Lifecycle.State.CREATED)
            fragmentTransaction.commit()
    

    表示fragment的只能执行到onCreate这个生命周期,而不能执行下一个生命周期,那么就无法执行onCreateView,也就无法显示出Fragment。如果是一个onResume的生命周期的Fragment被设置为fragmentTransaction.setMaxLifecycle(fragment, Lifecycle.State.CREATED),那么这个Fragment就会调用onPause、onStop、onDestroyView,也就是回退到了onCreate状态
    如果是设置为RESUMED

            fragment = TestLifecycleFragment()
            val fragmentTransaction = supportFragmentManager.beginTransaction()
            fragmentTransaction.add(R.id.ll_fragment, fragment)
            fragmentTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED)
            fragmentTransaction.commit()
    

    那么这个fragment就能正常显示出来,且Fragment可以执行到onResume这个生命周期
    如果是设置为STARTED

            fragment = TestLifecycleFragment()
            val fragmentTransaction = supportFragmentManager.beginTransaction()
            fragmentTransaction.add(R.id.ll_fragment, fragment)
            fragmentTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED)
            fragmentTransaction.commit()
    

    那么Fragment只能执行到onStart,如果是一个onResume的Fragment设置为STARTED,那么Fragment就会调用onPause,即回退到了onStart状态

    2.结合ViewPager实现懒加载

    在FragmentStatePagerAdapter中有一个构造器,即:

    public FragmentStatePagerAdapter(@NonNull FragmentManager fm,
            @Behavior int behavior) {
        mFragmentManager = fm;
        mBehavior = behavior;
    }
    

    可以看到这个构造器中传递了一个Behavior的int值,这个Behavior是在FragmentStatePagerAdapter中定义的一个泛型。

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({BEHAVIOR_SET_USER_VISIBLE_HINT, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT})
    private @interface Behavior { }
    

    而BEHAVIOR_SET_USER_VISIBLE_HINT和BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT是在FragmentStatePagerAdapter中定义的静态常量。
    这两个常量的使用是在FragmentStatePagerAdapter.setPrimaryItem方法中。

    @Override
    @SuppressWarnings({"ReferenceEquality", "deprecation"})
    public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        Fragment fragment = (Fragment)object;
        if (fragment != mCurrentPrimaryItem) {
            if (mCurrentPrimaryItem != null) {
                mCurrentPrimaryItem.setMenuVisibility(false);
                if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                    if (mCurTransaction == null) {
                        mCurTransaction = mFragmentManager.beginTransaction();
                    }
                    mCurTransaction.setMaxLifecycle(mCurrentPrimaryItem, Lifecycle.State.STARTED);
                } else {
                    mCurrentPrimaryItem.setUserVisibleHint(false);
                }
            }
            fragment.setMenuVisibility(true);
            if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                if (mCurTransaction == null) {
                    mCurTransaction = mFragmentManager.beginTransaction();
                }
                mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED);
            } else {
                fragment.setUserVisibleHint(true);
            }
    
            mCurrentPrimaryItem = fragment;
        }
    }
    

    而FragmentStatePagerAdapter.setPrimaryItem方法的调用,是在ViewPager.populate方法中,即要显示item的时候调用setPrimaryItem。
    当Behavior使用BEHAVIOR_SET_USER_VISIBLE_HINT的时候,即Fragment会回调setUserVisibleHint。当Behavior使用BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT的时候,意味着只有当前显示的Fragment会被执行到onResume,而其他的Fragment的生命周期只会执行到onStart。
    这样一来,如果想要做懒加载,则其实可以针对Fragment的生命周期onResume来实现,当Fragment需要显示的时候,调用FragmentTransaction.setMaxLifecycle设置为RESUME,当不需要显示的时候,设置为onStart,如果是不使用ViewPager或者ViewPager2的话。
    如果是使用ViewPager的话,则在onResume做数据的加载,不在onStart和onViewCreated做数据的加载,如果只需要主动加载第一次加载,则只需要一个变量判断是否是第一次加载即可。
    即在自定义FragmentStatePagerAdapter的时候,使用其两个参数的构造器,然后给Behavior传入FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT即可,然后在Fragment的onResume生命周期内执行数据加载的操作,这样就可以实现懒加载,只有当前Fragment会去加载。并且如果只需要加载一次,则可以使用一个变量判断是否是第一次加载数据,如果不是则不加载数据即可。

    五、FragmentStatePagerAdapter源码解析

    @SuppressWarnings("deprecation")
    public abstract class FragmentStatePagerAdapter extends PagerAdapter {
        private static final String TAG = "FragmentStatePagerAdapt";
        private static final boolean DEBUG = false;
    
        @Retention(RetentionPolicy.SOURCE)
        @IntDef({BEHAVIOR_SET_USER_VISIBLE_HINT, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT})
        private @interface Behavior { }
    
        /**
         * Indicates that {@link Fragment#setUserVisibleHint(boolean)} will be called when the current
         * fragment changes.
         *
         * @deprecated This behavior relies on the deprecated
         * {@link Fragment#setUserVisibleHint(boolean)} API. Use
         * {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT} to switch to its replacement,
         * {@link FragmentTransaction#setMaxLifecycle}.
         * @see #FragmentStatePagerAdapter(FragmentManager, int)
         */
        @Deprecated
        public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0;
    
        /**
         * Indicates that only the current fragment will be in the {@link Lifecycle.State#RESUMED}
         * state. All other Fragments are capped at {@link Lifecycle.State#STARTED}.
         *
         * @see #FragmentStatePagerAdapter(FragmentManager, int)
         */
        public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1;
    
        private final FragmentManager mFragmentManager;
        private final int mBehavior;
        private FragmentTransaction mCurTransaction = null;
    
        private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<>();
        private ArrayList<Fragment> mFragments = new ArrayList<>();
        private Fragment mCurrentPrimaryItem = null;
        private boolean mExecutingFinishUpdate;
    
        /**
         * Constructor for {@link FragmentStatePagerAdapter} that sets the fragment manager for the
         * adapter. This is the equivalent of calling
         * {@link #FragmentStatePagerAdapter(FragmentManager, int)} and passing in
         * {@link #BEHAVIOR_SET_USER_VISIBLE_HINT}.
         *
         * <p>Fragments will have {@link Fragment#setUserVisibleHint(boolean)} called whenever the
         * current Fragment changes.</p>
         *
         * @param fm fragment manager that will interact with this adapter
         * @deprecated use {@link #FragmentStatePagerAdapter(FragmentManager, int)} with
         * {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT}
         */
        @Deprecated
        public FragmentStatePagerAdapter(@NonNull FragmentManager fm) {
            this(fm, BEHAVIOR_SET_USER_VISIBLE_HINT);
        }
    
        /**
         * Constructor for {@link FragmentStatePagerAdapter}.
         *
         * If {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT} is passed in, then only the current
         * Fragment is in the {@link Lifecycle.State#RESUMED} state, while all other fragments are
         * capped at {@link Lifecycle.State#STARTED}. If {@link #BEHAVIOR_SET_USER_VISIBLE_HINT} is
         * passed, all fragments are in the {@link Lifecycle.State#RESUMED} state and there will be
         * callbacks to {@link Fragment#setUserVisibleHint(boolean)}.
         *
         * @param fm fragment manager that will interact with this adapter
         * @param behavior determines if only current fragments are in a resumed state
         */
        public FragmentStatePagerAdapter(@NonNull FragmentManager fm,
                @Behavior int behavior) {
            mFragmentManager = fm;
            mBehavior = behavior;
        }
    
        /**
         * Return the Fragment associated with a specified position.
         */
        @NonNull
        public abstract Fragment getItem(int position);
    
        @Override
        public void startUpdate(@NonNull ViewGroup container) {
            if (container.getId() == View.NO_ID) {
                throw new IllegalStateException("ViewPager with adapter " + this
                        + " requires a view id");
            }
        }
    
        @SuppressWarnings("deprecation")
        @NonNull
        @Override
        public Object instantiateItem(@NonNull ViewGroup container, int position) {
            // If we already have this item instantiated, there is nothing
            // to do.  This can happen when we are restoring the entire pager
            // from its saved state, where the fragment manager has already
            // taken care of restoring the fragments we previously had instantiated.
            if (mFragments.size() > position) {
                Fragment f = mFragments.get(position);
                if (f != null) {
                    return f;
                }
            }
    
            if (mCurTransaction == null) {
                mCurTransaction = mFragmentManager.beginTransaction();
            }
    
            Fragment fragment = getItem(position);
            if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
            if (mSavedState.size() > position) {
                Fragment.SavedState fss = mSavedState.get(position);
                if (fss != null) {
                    fragment.setInitialSavedState(fss);
                }
            }
            while (mFragments.size() <= position) {
                mFragments.add(null);
            }
            // 调用fragment的对应的方法
            // 比如setUserVisibleHint
            // 此时将fragment添加到事务中,但是还没提交事务
            // 优先调用了setUserVisibleHint,而提交事务是在finishUpdate中
            // 只有提交事务了才会执行生命周期
            fragment.setMenuVisibility(false);
            if (mBehavior == BEHAVIOR_SET_USER_VISIBLE_HINT) {
                fragment.setUserVisibleHint(false);
            }
    
            mFragments.set(position, fragment);
            mCurTransaction.add(container.getId(), fragment);
    
            if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED);
            }
    
            return fragment;
        }
    
        // TODO(b/141958824): Suppressed during upgrade to AGP 3.6.
        @SuppressWarnings("ReferenceEquality")
        @Override
        public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
            Fragment fragment = (Fragment) object;
    
            if (mCurTransaction == null) {
                mCurTransaction = mFragmentManager.beginTransaction();
            }
            if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
                    + " v=" + ((Fragment)object).getView());
            while (mSavedState.size() <= position) {
                mSavedState.add(null);
            }
            mSavedState.set(position, fragment.isAdded()
                    ? mFragmentManager.saveFragmentInstanceState(fragment) : null);
            mFragments.set(position, null);
    
            mCurTransaction.remove(fragment);
            if (fragment.equals(mCurrentPrimaryItem)) {
                mCurrentPrimaryItem = null;
            }
        }
    
        @Override
        @SuppressWarnings({"ReferenceEquality", "deprecation"})
        public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
            Fragment fragment = (Fragment)object;
            if (fragment != mCurrentPrimaryItem) {
                // 在切换ViewPager的时候,会调用adapter的setPrimaryItem方法
                // 切换的时候,会将当前Fragment的setUserVisibleHint设置为false
                // 会将目标fragment的setUserVisibleHint设置为true
                // 即当前fragment需要隐藏,目标fragment需要显示。
                if (mCurrentPrimaryItem != null) {
                    mCurrentPrimaryItem.setMenuVisibility(false);
                    if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                        if (mCurTransaction == null) {
                            mCurTransaction = mFragmentManager.beginTransaction();
                        }
                        mCurTransaction.setMaxLifecycle(mCurrentPrimaryItem, Lifecycle.State.STARTED);
                    } else {
                        mCurrentPrimaryItem.setUserVisibleHint(false);
                    }
                }
                fragment.setMenuVisibility(true);
                if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                    if (mCurTransaction == null) {
                        mCurTransaction = mFragmentManager.beginTransaction();
                    }
                    mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED);
                } else {
                    fragment.setUserVisibleHint(true);
                }
    
                mCurrentPrimaryItem = fragment;
            }
        }
    
        @Override
        public void finishUpdate(@NonNull ViewGroup container) {
            if (mCurTransaction != null) {
                // We drop any transactions that attempt to be committed
                // from a re-entrant call to finishUpdate(). We need to
                // do this as a workaround for Robolectric running measure/layout
                // calls inline rather than allowing them to be posted
                // as they would on a real device.
                if (!mExecutingFinishUpdate) {
                    // 提交事务
                    // 在ViewPager.populate方法中调用
                    // 当执行完Adapter的startUpdate、addNewItem
                    // instantiateItem、destroyItem、setPrimaryItem
                    // 之后,才会调用finishUpdate
                    try {
                        mExecutingFinishUpdate = true;
                        mCurTransaction.commitNowAllowingStateLoss();
                    } finally {
                        mExecutingFinishUpdate = false;
                    }
                }
                mCurTransaction = null;
            }
        }
    
        @Override
        public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
            return ((Fragment)object).getView() == view;
        }
    
        @Override
        @Nullable
        public Parcelable saveState() {
            Bundle state = null;
            if (mSavedState.size() > 0) {
                state = new Bundle();
                Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
                mSavedState.toArray(fss);
                state.putParcelableArray("states", fss);
            }
            for (int i=0; i<mFragments.size(); i++) {
                Fragment f = mFragments.get(i);
                if (f != null && f.isAdded()) {
                    if (state == null) {
                        state = new Bundle();
                    }
                    String key = "f" + i;
                    mFragmentManager.putFragment(state, key, f);
                }
            }
            return state;
        }
    
        @Override
        public void restoreState(@Nullable Parcelable state, @Nullable ClassLoader loader) {
            if (state != null) {
                Bundle bundle = (Bundle)state;
                bundle.setClassLoader(loader);
                Parcelable[] fss = bundle.getParcelableArray("states");
                mSavedState.clear();
                mFragments.clear();
                if (fss != null) {
                    for (int i=0; i<fss.length; i++) {
                        mSavedState.add((Fragment.SavedState)fss[i]);
                    }
                }
                Iterable<String> keys = bundle.keySet();
                for (String key: keys) {
                    if (key.startsWith("f")) {
                        int index = Integer.parseInt(key.substring(1));
                        Fragment f = mFragmentManager.getFragment(bundle, key);
                        if (f != null) {
                            while (mFragments.size() <= index) {
                                mFragments.add(null);
                            }
                            f.setMenuVisibility(false);
                            mFragments.set(index, f);
                        } else {
                            Log.w(TAG, "Bad fragment at key " + key);
                        }
                    }
                }
            }
        }
    }
    

    从分析FragmentStatePagerAdapter来看,setUserVisibleHint方法会优先于Fragment的生命周期函数 执行。因为在FragmentStatePagerAdapter中提交事务,是在调用finishUpdate方法中进行的,只有提交事务的时候,才会去执行Fragment的生命周期。

    参考

    《学不动也要学!探究Fragment延迟加载的前世今生》

    相关文章

      网友评论

          本文标题:Android-Fragment的生命周期、FragmentSt

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