这是我参与11月更文挑战的第16天,活动详情查看: 2021最后一次更文挑战

相信很多码友都经常使用到ViewPager + Fragment的组合,肯定也有需求需要对Fragment做懒加载,增强用户体验,那么Fragment如何实现懒加载呢

Fragment 懒加载的方式

借助setUserVisiblity实现,具体实现方式如下:

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (getUserVisibleHint()) {
        isVisible = true;
        onVisible();
    } else {
        isVisible = false;
        onInVisible();

是不是很熟悉,但是有没有发现,setUserVisiblity这个方法上多了一个注解@Deprecated,是的,它已经过时了,那么替代它的方法是什么呢,在源码里可以清楚看到:

* @return The current value of the user-visible hint on this fragment. * @see #setUserVisibleHint(boolean) * @deprecated Use {@link FragmentTransaction#setMaxLifecycle(Fragment, Lifecycle.State)} * instead. @Deprecated public boolean getUserVisibleHint() { return mUserVisibleHint;

对,你没有看错是setMaxLifecycle这个方法。

AndroidX 自 1.1.0-alpha07 起, 为FragmentTransaction增加了新的方法setMaxLifeCycle, 官方建议开发者以此取代setUserVisibleHint,因为这将带来如下好处:

  • 基于Lifecycle的懒加载更加科学,可以配合Livedata等组件在MVVM架构中使用
  • setMaxLifeCycle 无需额外定义Fragment基类,使用起来更加无侵
  • 那么怎么使用setMaxLifecycle来实现fragment的懒加载呢

    setMaxLifecycle的使用方式

    FragmentPagerAdapter 的构造方法中新增了一个 behavior 参数, 当参数设置为FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT时,就会使用setMaxLifecycle 来限制了 Fragment 的生命周期,只有当 Fragment 显示在屏幕中时才会执行onResume(),这样就可以把加载数据的方法放在 onResume() 中从而实现懒加载:

    class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
            super.onCreate(savedInstanceState, persistentState)
            setContentView(R.layout.activity_main)
            val viewPager: ViewPager = findViewById(R.id.viewpager)
            val fragmentList: MutableList<Fragment> = ArrayList()
            fragmentList.add(Fragment1())
            fragmentList.add(Fragment2())
            fragmentList.add(Fragment3())
            // 为MyPagerAdapter适配器设置FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT 参数
            val myPagerAdapter: MyPagerAdapter = MyPagerAdapter(
                getSupportFragmentManager(),
                FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT, fragmentList
            viewPager.setAdapter(myPagerAdapter)
            // 设置预加载为3页,来测试懒加载是否成功
            viewPager.offscreenPageLimit = 3
        class MyPagerAdapter(
            fm: FragmentManager,
            behavior: Int,
            val fragmentList: List<Fragment>
            FragmentPagerAdapter(fm, behavior) {
            override fun getCount() = fragmentList.size
            override fun getItem(position: Int) = fragmentList[position]
    

    FragmentPagerAdapter 在创建 Fragment后,根据 behavior 调用了setMaxLifecycle。

    //FragmentPagerAdapter.java
     public FragmentPagerAdapter(@NonNull FragmentManager fm,
            @Behavior int behavior) {
        mFragmentManager = fm;
        mBehavior = behavior;
    @Override
    public Object instantiateItem(@NonNull ViewGroup container, int position) {
        if (fragment != mCurrentPrimaryItem) {
            fragment.setMenuVisibility(false);
            // mBehaviour为1的时候走新逻辑
            if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                // 初始化item时将其生命周期限制为STARTED
                mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED);
            } else {
                // 兼容旧版逻辑
                fragment.setUserVisibleHint(false);
        return fragment;
    @Override
    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) {
                    // 滑走的会变成非主item, 设置其Lifecycle为STARTED
                    mCurTransaction.setMaxLifecycle(mCurrentPrimaryItem, Lifecycle.State.STARTED);
                } else {
                    mCurrentPrimaryItem.setUserVisibleHint(false);
            fragment.setMenuVisibility(true);
            if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                // 设置新滑到的主item的Lifecycle为RESUMED
                mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED);
            } else {
                fragment.setUserVisibleHint(true);
            mCurrentPrimaryItem = fragment;
    

    通过源码可以知道,即使不借助behavior,在自定义Adapter中构建 Framgent时直接调用setMaxLifecycle 也是等价的。

    setMaxLifecycle 实现懒加载的的原理

    FramgentTransition 对 Fragment 的所有操作都将转换为一个Op,针对setMaxLifecycle也同样增加了一个新的Op -- OP_SET_MAX_LIFECYCLE, 专门用来处理对生命周期的限制。当 FramgentTransition 对某个 Frament 添加了 OP_SET_MAX_LIFECYCLE 后,在其实现类BackStackRecord 中, FragmentManager 会遍历 Transaction 的 Op列表,设置 Fragment 的 mMaxState 表明其被允许的最大生命周期。mMaxState 的设置是通过在 FragmentManager 中的同名方法 setMaxLifeCycle 完成的。### FragmentStateManager

    调用 setMaxLifecycle 之后, FragmentManager 会通过 FragmentStateManager 对 Fragment 生命周期做出限制。

    值得一提的是,FragmentStateManager 是 1.3.0-alpha08 之后新增的类,将原来和 State 相关的逻辑从FragmentManager 抽离了出来, 减少了很多与 Fragment 的耦合, 职责更加单一。

    除了使用默认的 BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT,我们甚至可以在自定义Adapter的instantiateItem 中为将 Fragment的MaxLifecycle 设置为 CREATED, 这样可以让 Fragment 只走到onCreate从而延迟更多操作, 比如在 onCreateView 中的 inflate 以及 onViewCreated 中的一些操作。

    分类:
    Android
    标签: