美文网首页
探索Androidx下来viewpager+fragment懒加

探索Androidx下来viewpager+fragment懒加

作者: 折青梅 | 来源:发表于2021-03-01 14:41 被阅读0次

    上一篇中,我们谈论了support包下的viewpager+fragment的懒加载方式懒加载,现在我们来分析下androidx下的这种结构如何实现懒加载

    依然是FragmentStatePagerAdapter

    在上一篇中,我们知道Viewpage加载fragment的入口是setAdapter,而与之相关的fragment初始化函数是在populate()这个函数
    ,不了解上一篇的也没关心,下面是Viewpager.setAdapter的源码

    Viewpager.setAdapter(PagerAdapter adapter)

     /**
         * Set a PagerAdapter that will supply views for this pager as needed.
         *
         * @param adapter Adapter to use
         */
        public void setAdapter(@Nullable PagerAdapter adapter) {
            if (mAdapter != null) {
              //对adapter初始化
            }
    
         ....
    
            if (mAdapter != null) {
               //初始化成员变量
                if (mRestoredCurItem >= 0) {
                   ....
                } else if (!wasFirstLayout) {
                  //关键:进行生命周期
                    populate();
                } else {
                    requestLayout();
                }
            }
    
            // Dispatch the change to any listeners
            if (mAdapterChangeListeners != null && !mAdapterChangeListeners.isEmpty()) {
                for (int i = 0, count = mAdapterChangeListeners.size(); i < count; i++) {
                    mAdapterChangeListeners.get(i).onAdapterChanged(this, oldAdapter, adapter);
                }
            }
        }
    

    而在populate函数中,进行了mAdapter的几个关键函数调用:
    startUpdate()->instantiateItem(addNewItem)->setPrimaryItem()->finishUpdate
    而FragmentStatePagerAdapter继承至PagerAdapter(Viewpager.setAdapter(PagerAdapter adapter))
    ,并重写了以上函数,所以,我们以FragmentStatePagerAdapter重写的以上函数做关键分析

    FragmentStatePagerAdapter

    打开FragmentStatePagerAdapter,三个函数基本相同,但是发现setUserVisibleHint被废弃了,之前分析setUserVisibleHint是Viewpager中setPrimaryItem函数调用,会设置为true,也就是frgament被当前Viewpager设置选中的时候,即我们懒加载优化方案可见的时候调用。

    setPrimaryItem

     @Override
        @SuppressWarnings({"ReferenceEquality", "deprecation"})
        public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
            Fragment fragment = (Fragment)object;
            //缓存的fragment不是当前选中的fragment时调用,即当前未选中->到选中
            if (fragment != mCurrentPrimaryItem) {
                ...
                if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                    if (mCurTransaction == null) {
                        mCurTransaction = mFragmentManager.beginTransaction();
                    }
                    mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED);
                } else {
                  //setUserVisibleHint(true)
                    fragment.setUserVisibleHint(true);
                }
                //替换缓存当前设置的fragment,和上面fragment != mCurrentPrimaryItem配置呼应
                mCurrentPrimaryItem = fragment;
            }
        }
    

    机智的我们,看到了setMaxLifecycle这个函数,而我们在setUserVisibleHint标记废弃的注释也看到了这个函数,下面是setUserVisibleHint函数的部分注释:英文战5渣的我,打开了有道翻译
    Set a hint to the system about whether this fragment's UI is currently visible to the user.
    这个函数是fragment的UI对用户可见是调用
    由于上文,分析了fragment在viewpager中的生命周期是在finishUpdate通过事务commit提交的

     @Override
        public void finishUpdate(@NonNull ViewGroup container) {
            if (mCurTransaction != null) {
                mCurTransaction.commitNowAllowingStateLoss();
                mCurTransaction = null;
            }
        }
    
    

    所以setUserVisibleHint是在生命周期之前调用的,它的注释也说明了这个问题
    This method may be called outside of the fragment lifecycle. and thus has no ordering guarantees with regard to fragment lifecycle method calls.
    这个函数是在fragment的生命周期之外的。然后被标记为废弃@Deprecated,并推荐我们使用
    @deprecated Use {@link FragmentTransaction#setMaxLifecycle(Fragment, Lifecycle.State)}instead
    代替,这就回到了上文的 mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED)函数


    very.strong.jpg

    ,也就是,在可见的时候,setMaxLifecycle为Lifecycle.State.RESUMED,光看函数意思,表示,可见是时候,设置生命周期为
    onResume,那么为什么是setMaxLifecycle,而不是setLifecycle呢?

    setMaxLifecycle

    再来看注释:
    Set a ceiling for the state of an active fragment in this FragmentManager.
    为fragment的生命周期设置一个上限,
    If fragment is already above the received state, it will be forced down to the correct state.
    超过这个生命周期的将被(forced down)强制下降。
    原来viewpager在androidx里是通过强制下降生命周期来控制的。


    很强.jpg

    那么他是如何做到通过最大生命周期来下降生命周期的呢?接着往下看

     @NonNull
        public FragmentTransaction setMaxLifecycle(@NonNull Fragment fragment,
                @NonNull Lifecycle.State state) {
            addOp(new Op(OP_SET_MAX_LIFECYCLE, fragment, state));
            return this;
        }
    

    addOp函数new了一个Op,这个op这个是FragmentTransaction在Adnroidx新增事务的一个内部类,用于这个内部类里记录了fragment,以及他的当前状态进出场动画等,有3个重载函数,重点看带参的

    //兼容以前的写法
     Op(int cmd, Fragment fragment) {
                this.mCmd = cmd;
                this.mFragment = fragment;
                //默认初始化的时候最大状态是onResume
                this.mOldMaxState = Lifecycle.State.RESUMED;
                //默认选中的时候最大状态是onResume
                this.mCurrentMaxState = Lifecycle.State.RESUMED;
            }
    
      Op(int cmd, @NonNull Fragment fragment, Lifecycle.State state) {
                this.mCmd = cmd;
                this.mFragment = fragment;
                //fragment中的初始化状态 Lifecycle.State mMaxState = Lifecycle.State.RESUMED;
                this.mOldMaxState = fragment.mMaxState;
                //赋值代签状态
                this.mCurrentMaxState = state;
            }
    

    我们在添加的fragment的时候,会getSupportFragmentManager

    getSupportFragmentManager

      @NonNull
        public FragmentManager getSupportFragmentManager() {
            return mFragments.getSupportFragmentManager();
        }
    

    mFragments.getSupportFragmentManager()

    @NonNull
        public FragmentManager getSupportFragmentManager() {
            return mHost.mFragmentManager;
        }
    final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
    

    我们找到FragmentManagerImpl类

    FragmentManagerImpl

    class FragmentManagerImpl extends FragmentManager
    

    可见fragmentManager是通过FragmentManagerImpl 这个类去代理管理生命周期的
    我们来寻找下他的事务

    beginTransaction

    @NonNull
        @Override
        public FragmentTransaction beginTransaction() {
            return new BackStackRecord(this);
        }
    

    可以看到这里new了一个BackStackRecord,而BackStackRecord继承自FragmentTransaction ,所以,最终执行事务的是
    BackStackRecord,现在深入BackStackRecord(this)

    BackStackRecord

    public BackStackRecord(FragmentManagerImpl manager) {
            mManager = manager;
        }
    

    我们知道,最后fragment通过事务FragmentTransaction调用commit函数才能初始化,所以我们查看commit函数

     @Override
        public int commit() {
            return commitInternal(false);
        }
    
        @Override
        public int commitAllowingStateLoss() {
            return commitInternal(true);
        }
    
        @Override
        public void commitNow() {
            disallowAddToBackStack();
            mManager.execSingleAction(this, false);
        }
    
        @Override
        public void commitNowAllowingStateLoss() {
            disallowAddToBackStack();
            mManager.execSingleAction(this, true);
        }
    

    commit实际执行了execSingleAction

    int commitInternal(boolean allowStateLoss) {
          ...
            //最后执行
            mManager.enqueueAction(this, allowStateLoss);
            return mIndex;
        }
    //enqueueAction
    public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
           ...
            synchronized (this) {
               ...
                scheduleCommit();
            }
        }
    //scheduleCommit
     void scheduleCommit() {
            synchronized (this) {
                boolean postponeReady =
                        mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
                boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
                if (postponeReady || pendingReady) {
                  //remove再post
                    mHost.getHandler().removeCallbacks(mExecCommit);
                    mHost.getHandler().post(mExecCommit);
                    updateOnBackPressedCallbackEnabled();
                }
            }
        }
    

    可以看到,内部执行是通过handler来执行的
    继续寻找,发现最后执行一个performPendingDeferredStart函数,内部调用moveToState函数能看到设置的maxLifecycle身影

    moveToState

    void moveToState(Fragment f, int newState, int transit, int transitionStyle,boolean keepActive) {
            // Don't allow the Fragment to go above its max lifecycle state
            // Ensure that Fragments are capped at CREATED instead of ACTIVITY_CREATED.
            if (f.mMaxState == Lifecycle.State.CREATED) {
                newState = Math.min(newState, Fragment.CREATED);
            } else {
                newState = Math.min(newState, f.mMaxState.ordinal());
            }
    }
    

    可以看到,当超过了设置的maxLifecycle生命周期,直接取赋值的生命周期,简单粗暴,感兴趣的同学,可以研究下moveToState函数,里面分发了所有fragment的生命周期事件,也就是之所以我们的onCreate,onCreateView,onResume等生命周期能监听到回调的原因,还有怎么恢复缓存的。
    所以,现在我们的懒加载在androidx下就显得极其简单了:

    viewpager.setAdapter

    FragmentStatePagerAdapter mAdapter = new FragmentStatePagerAdapter(getChildFragmentManager(), BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                @NonNull
                @Override
                public Fragment getItem(int position) {
                    return fragmentList.get(position);
                }
    
                @Override
                public int getCount() {
                    return fragmentList.size();
                }
            };
     mViewPage.setAdapter(mAdapter);
    

    在FragmentStatePagerAdapter 函数中,设置了mBehavior==BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT则不会再走setUserVisibleHint函数,我们再看被选中(可见)setPrimaryItem函数

    if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                    if (mCurTransaction == null) {
                        mCurTransaction = mFragmentManager.beginTransaction();
                    }
                    mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED);
                } else {
                    fragment.setUserVisibleHint(true);
                }
    

    这里设置了maxLifecycle== Lifecycle.State.RESUMED,这就和activity可见的生命周期相同,控制懒加载也就极其简单

      /**
         * 当前的activity是否已经可见
         */
        private boolean currentVisibleStatus = false;
    @Override
        public void onResume() {
            super.onResume();
            Log.i(TAG, getClass().getSimpleName() + "==>onResume");
            if (currentVisibleStatus) {
                dispatchVisibleStatus(false);
            } else {
                dispatchVisibleStatus(true);
            }
        }
    
        @Override
        public void onPause() {
            super.onPause();
            Log.i(TAG, getClass().getSimpleName() + "==>onResume");
            if (currentVisibleStatus) {
                dispatchVisibleStatus(false);
            }
        }
     @Override
        public void setUserVisibleHint(boolean isVisibleToUser) {
            super.setUserVisibleHint(isVisibleToUser);
            Log.i(TAG, "==>setUserVisibleHint");
        }
    

    我们运行下看下log:


    androidlifecycle.gif
    log.png

    可以看到setUserVisibleHint并未执行log打印,并且在嵌套模式下以依然可以正常分发到子fragment
    完整代码

    LazyFragmentx

    /**
     * @author chenke
     * @create 2021/2/25
     * @Describe AndroidX中的fragment懒加载
     */
    public abstract class LazyFragmentX extends Fragment {
        private final String TAG = "lazy_x";
    
        /**
         * 抽象布局
         *
         * @return
         */
        public abstract int getLayout();
    
        /**
         * @param view 初始化view
         */
        public abstract void initView(View view);
    
        private View mRootView;
        /**
         * 当前的activity是否已经可见
         */
        private boolean currentVisibleStatus = false;
    
        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            if (mRootView == null) {
                mRootView = inflater.inflate(getLayout(), container, false);
            }
            initView(mRootView);
            Log.i(TAG, getClass().getSimpleName() + "==>onCreateView");
            return mRootView;
        }
    
        @Override
        public void setUserVisibleHint(boolean isVisibleToUser) {
            super.setUserVisibleHint(isVisibleToUser);
            Log.i(TAG, "==>setUserVisibleHint");
        }
    
        @Override
        public void onStart() {
            super.onStart();
            Log.i(TAG, getClass().getSimpleName() + "==>onStart");
        }
    
        @Override
        public void onResume() {
            super.onResume();
            Log.i(TAG, getClass().getSimpleName() + "==>onResume");
            if (currentVisibleStatus) {
                dispatchVisibleStatus(false);
            } else {
                dispatchVisibleStatus(true);
            }
        }
    
        @Override
        public void onPause() {
            super.onPause();
            Log.i(TAG, getClass().getSimpleName() + "==>onResume");
            if (currentVisibleStatus) {
                dispatchVisibleStatus(false);
            }
        }
    
        @Override
        public void onStop() {
            super.onStop();
            Log.i(TAG, getClass().getSimpleName() + "==>onResume");
        }
    
        public void dispatchVisibleStatus(boolean visibleStatus) {
            this.currentVisibleStatus = visibleStatus;
            if (visibleStatus) {
                onStartLoad();
            } else {
                onStopLoad();
            }
        }
    
        public void onStartLoad() {
            Log.i(TAG, getClass().getSimpleName() + "==>onStartLoad加载数据");
        }
    
        public void onStopLoad() {
            Log.i(TAG, getClass().getSimpleName() + "==>onStopLoad暂停加载");
        }
    }
    

    总结:
    1:androidx,对于viewpager+fragment设计的界面,再创建FragmentStatePagerAdapter是调用
    FragmentStatePagerAdapter(@NonNull FragmentManager fm,
    @Behavior int behavior)构造函数,并且传入BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT或者1
    2:Fragment的生命周期是通过主线程的handler来轮询分发处理的
    3:setMaxLifecycle的原理就是直接将超过set进去的生命周期,赋值为设置的生命周期,具体逻辑在FragmentManagerImpl.moveToState函数中
    4:moveToState控制了所有的fragment的生命周期的调用分发
    项目地址:
    demo地址:github

    相关文章

      网友评论

          本文标题:探索Androidx下来viewpager+fragment懒加

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