• Android Fragment 懒加载


    Android Fragment 懒加载

    一、为什么要进行懒加载

    一般我们在使用add+show+hide去显示、隐藏fragment或者fragment嵌套使用、viewpager+fragment结合使用等场景下,如果不进行懒加载会导致多个fragment页面的生命周期被调用,每个页面都进行网络请求这样会产生很多无用的请求,因为实际显示的只是用户看到的那个页面,其他页面没有必要在这个时候去加载数据。

    二、fragment懒加载

    懒加载即在页面第一次可见时再进行数据请求以及加载的操作,因为版本的不同实现懒加载的方式也略有不同。

    首先我们来看下旧版(support包)懒加载的实现方式,其实旧版主要是利用setUserVisibleHint和onHiddenChanged两个函数来实现懒加载的。

    • viewpager+fragment

    viewpager+fragment结合的方式,一共有4个fragment分别是fragmentA、fragmentB、fragmentC、fragmentD。
    ViewPager 预缓存 Fragment 的个数为1的情况下fragment的生命周期变化。

    右划到fragmentB时的变化

    返回fragmentA时的变化

    由上面我们可以看出每次setUserVisibleHint函数都会比fragment的生命周期函数先回调,且仅当前显示页面的isVisibleToUser为true,其他页面isVisibleToUser均为false。进入fragmentA页面时同时加载了fragmentA和fragmentB且两者都回调了onresume生命周期函数,当划向fragmentB之后加载了fragmentC但是fragmentB未回调任何生命周期函数仅回调了setUserVisibleHint。

    因此我们可以利用setUserVisibleHint来进行fragment的懒加载。下面上代码

    public class LazyLoadFragment extends Fragment {
        //判断是否已进行过加载,避免重复加载
        private boolean isLoad=false;
        //判断当前fragment是否可见
        private boolean isVisibleToUser = false;
        //判断当前fragment是否回调了resume
        private boolean isResume = false;
        @Override
        public void onResume() {
            super.onResume();
            isResume=true;
            lazyLoad();
        }
    
        @Override
        public void setUserVisibleHint(boolean isVisibleToUser) {
            super.setUserVisibleHint(isVisibleToUser);
            Log.i(TAG, “setUserVisibleHint: “+isVisibleToUser+”  “+FragmentD.this);
            this.isVisibleToUser=isVisibleToUser;
            lazyLoad();
        }
    
        private void lazyLoad() {
            if (!isLoad&&isVisibleToUser&&isResume){
                //懒加载。。。
                isLoad=true;
            }
        }
    
    • add+show+hide
      Add fragmentA和fragmentB然后hide B show A,下面是两者相关函数回调结果

      然后 show B hide A

    再show A hide B

    右上边可以看出一开始add A和B时两者都回调了onresume,但是B还 回调了onHiddenChanged且为true,之后show B hide A 没有触发任何生命周期函数,两者仅回调了onHiddenChanged 且A为true B为false。

    由此我们可以利用onHiddenChanged 来完成懒加载机制,下面是代码

    public class FragmentD extends Fragment {
        //判断是否已进行过加载,避免重复加载
        private boolean isLoad=false;
        //判断当前fragment是否可见
        private boolean isHidden=true;
    
        @Override
        public void onResume() {
            super.onResume();
            lazyLoad();
        }
        @Override
        public void onHiddenChanged(boolean hidden) {
            super.onHiddenChanged(hidden);
            Log.i(TAG, “onHiddenChanged: “+hidden+” “+FragmentD.this);
            isHidden=hidden;
            lazyLoad();
        }
    
        @Override
        public void onDestroyView() {
            super.onDestroyView();
            isLoad=false; //注意当view销毁时需要把isLoad置为false
        }
    
        private void lazyLoad() {
            if (!isLoad&&!isHidden){
                //懒加载。。。
                isLoad=true;
            }
        }
    }
    
    • 复杂嵌套的fragment
      当add+hide+show和viewpager+fragment 嵌套组合使用时上面的懒加载就需要做一些调整。
    public class FragmentD extends Fragment {
        //判断是否已进行过加载,避免重复加载
        private boolean isLoad=false;
        //判断当前fragment是否可见
        private boolean isVisibleToUser = false;
        //判断当前fragment是否回调了resume
        private boolean isResume = false;
        private boolean isCallUserVisibleHint = false;
    
        @Override
        public void onResume() {
            super.onResume();
            isResume=true;
            if (!isCallUserVisibleHint) isVisibleToUser=!isHidden();
            lazyLoad();
        }
    
        @Override
        public void setUserVisibleHint(boolean isVisibleToUser) {
            super.setUserVisibleHint(isVisibleToUser);
            Log.i(TAG, “setUserVisibleHint: “+isVisibleToUser+”  “+FragmentD.this);
            this.isVisibleToUser=isVisibleToUser;
            isCallUserVisibleHint=true;
            lazyLoad();
        }
    
        @Override
        public void onHiddenChanged(boolean hidden) {
            super.onHiddenChanged(hidden);
            Log.i(TAG, "onHiddenChanged: "+hidden+" "+FragmentD.this);
            isVisibleToUser=!hidden;
            lazyLoad();
        }
    
        @Override
        public void onDestroyView() {
            super.onDestroyView();
            isLoad=false;
            isCallUserVisibleHint=false;
            isVisibleToUser=false;
            isResume=false;
        }
    
        private void lazyLoad() {
            if (!isLoad&&isVisibleToUser&&isResume){
                //懒加载。。。
                isLoad=true;
            }
        }
    

    看完旧版的我们来看下新版的(Androidx)fragment的懒加载应该如何实现。

    Google 在 Androidx 在FragmentTransaction中增加了setMaxLifecycle方法来控制 Fragment 所能调用的最大的生命周期函数.该方法可以设置活跃状态下 Fragment 最大的状态,如果该 Fragment 超过了设置的最大状态,那么会强制将 Fragment 降级到正确的状态。

    • ViewPager+Fragment
      在 FragmentPagerAdapter 与 FragmentStatePagerAdapter 新增了含有behavior字段的构造函数
      public FragmentPagerAdapter(@NonNull FragmentManager fm,
                @Behavior int behavior) {
            mFragmentManager = fm;
            mBehavior = behavior;
        }
    
      public FragmentStatePagerAdapter(@NonNull FragmentManager fm,
                @Behavior int behavior) {
            mFragmentManager = fm;
            mBehavior = behavior;
        }
    
    

    如果 behavior 的值为 BEHAVIOR_SET_USER_VISIBLE_HINT,那么当 Fragment 对用户的可见状态发生改变时,setUserVisibleHint 方法会被调用。
    如果 behavior 的值为 BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT ,那么当前选中的 Fragment 在 Lifecycle.State RESUMED 状态 ,其他不可见的 Fragment 会被限制在 Lifecycle.State STARTED 状态。

    使用了 BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT 后,确实只有当前可见的 Fragment 调用了 onResume 方法。而导致产生这种改变的原因,是因为 FragmentPagerAdapter 在其 setPrimaryItem 方法中调用了 setMaxLifecycle 方法

        public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
            Fragment fragment = (Fragment)object;
            //如果当前的fragment不是当前选中并可见的Fragment,那么就会调用
            // setMaxLifecycle 设置其最大生命周期为 Lifecycle.State.STARTED
            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,则设置其最大生命周期为
            //Lifecycle.State.RESUMED
                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;
            }
        }
    

    所以在ViewPager+fragment 结构中实现懒加载可以这样:

    public class LazyLoadFragment extends Fragment {
        //判断是否已进行过加载,避免重复加载
        private boolean isLoad=false;
    
        @Override
        public void onResume() {
            super.onResume();
            if (!isLoad){
                lazyLoad();
                isLoad=true;
            }
        }
    
        @Override
        public void onDestroyView() {
            super.onDestroyView();
            isLoad=false;
        }
    
        private void lazyLoad() {
                //懒加载。。。
        }
    }
    
    • add+hide+show
      参考viewpager做法,在add fragment时仅把要显示的fragment通过setMaxLifecycle设置为resume,其他fragment均设置为start。
      在show、hide切换显示的fragment时仅把show的fragment通过setMaxLifecycle设置为resume,其他hide的fragment均设置为start

    • 复杂嵌套
      当fragment嵌套fragment等复杂情况下,只要父fragment回调onresume生命周期函数那被嵌套的所有同级子fragment都会回调onresume,所以我们需要再加上fragment是否隐藏来判断是否要进行懒加载。

    public class LazyLoadFragment extends Fragment {
        //判断是否已进行过加载,避免重复加载
        private boolean isLoad=false;
    
        @Override
        public void onResume() {
            super.onResume();
            if (!isLoad&& !isHidden()){
                lazyLoad();
                isLoad=true;
            }
        }
    
        @Override
        public void onDestroyView() {
            super.onDestroyView();
            isLoad=false;
        }
    
        private void lazyLoad() {
                //懒加载。。。
        }
    }
    

    viewpager2 本身已支持懒加载

  • 相关阅读:
    LeetCode 206. Reverse Linked List倒置链表 C++
    spring security 5 There is no PasswordEncoder mapped for the id "null" 错误
    jdk 1.8下 java ArrayList 添加元素解析
    qt (5.60/5.70) 编译 QOCI 驱动
    ps快捷键
    使用jquery中height()方法获取各种高度
    事件委托live,delegate,on区别
    ajax获取数据后怎么去渲染到页面?
    JavaScript:document.execCommand()的用法
    兼容IE FF 获取鼠标位置
  • 原文地址:https://www.cnblogs.com/Robin132929/p/13819386.html
Copyright © 2020-2023  润新知