Android-JetpackAndroid开发Android开发经验谈

LifeCycle在Fragment中的使用

2020-03-02  本文已影响0人  珠穆朗玛小王子

前言

今天在浏览技术大牛的公众号文章,看到一篇文章讲解了如何使用LifeCycle实现懒加载的新思路,经过学习和总结写一篇播放博客分享给大家。

原文作者博客:https://juejin.im/post/5e085dafe51d45580769a1eb
(此文章已授权鸿洋公众号)

再为大家推荐两位大牛的公众号,对于处于突破拔高期的朋友非常有帮助:
1、鸿洋大牛也是CSDN的知名博主:https://me.csdn.net/lmj623565791

鸿洋公众号
2、郭霖大牛博客地址:https://me.csdn.net/sinyu890807
郭霖公众号

正文

对于懒加载的实现新思路大家可以详细阅读原文,我这里只是一个学习总结的笔记。首先LifeCycle,主要是给Activity和Fragment对外暴露自己的生命周期的媒介,这样一些跟页面无关的逻辑就方便单独处理,例如常见的客户端维护的Activity堆栈,在之前我们大部分都会写在BaseActivity的周期中,现在通过LifeCycle,我们可以直接放在Application中单独处理。

今天我们不讨论LifeCycle的用法,只探讨LifeCycle对于Fragment的生命周期的影响。

Fragment问题1:Fragment创建的主要生命周期:

onAttach -> onCreate -> onCreateView -> onStart -> onResume

Fragment问题2:如何改变这个周期,例如创建的时候只执行到onCreate 或者 onStart,不会执行onResume?

如果是以前我相信我绝对答不出来。如果想要实现这个需求,我们可以通过:

FragmentTransaction.setMaxLifecycle(Fragment,  Lifecycle.State)

第一个参数是要设置的Fragment,不需要解释;
第二个参数是一个枚举:

  public enum State {
        DESTROYED,
        INITIALIZED,
        CREATED,
        STARTED,
        RESUMED;
        ...
        public boolean isAtLeast(@NonNull State state) {
            return compareTo(state) >= 0;
        }
    }
}

其中最常用的是 CREATED,STARTED,RESUMED,如果我们设置了CREATED,你会发现Fragment创建时的生命周期变为:

onAttach -> onCreate

如果设置的是STARTED:

onAttach -> onCreate -> onCreateView -> onStart

为什么setMaxLifecycle可以做到这么神奇的操作,我们从源码做一个简单的分析(源码的流程实在是太多了):

1、把操作保存到列表中

@NonNull
public FragmentTransaction setMaxLifecycle(@NonNull Fragment fragment,
            @NonNull Lifecycle.State state) {
   // 添加到列表,保存起来
   addOp(new Op(OP_SET_MAX_LIFECYCLE, fragment, state));
   return this;
}

2、调用commit,把设置的最大生命周期绑定到Fragment中

FragmentTransaction:
void executeOps() {
    ...
    mManager.setMaxLifecycle(f, op.mCurrentMaxState);
    break;
    ...
}

FragmentManager:
public void setMaxLifecycle(Fragment f, Lifecycle.State state) {
        // 请注意,Lifecycle.State.CREATED为最小值,所以不支持 DESTROYED,INITIALIZED,
        if (!state.isAtLeast(Lifecycle.State.CREATED)) {
            throw new IllegalArgumentException("Cannot set maximum Lifecycle below "
                    + Lifecycle.State.CREATED);
        }
     ...
     f.mMaxState = state;
}

这里要注意这个判断,Lifecycle.State的最小值是CREATED,所以不支持DESTROYED和INITIALIZED。

3、在周期分发的时候,根据MaxLifeState分发周期:

FragmentActivity:
// 取最小值,保证周期不会超出范围
 if (f.mMaxState == Lifecycle.State.CREATED) {
        newState = Math.min(newState, Fragment.CREATED);
} else {
        newState = Math.min(newState, f.mMaxState.ordinal());
}
// 判断是否小于最大周期
if (f.mState <= newState) {
    ... 分发周期
}

Fragment问题3:如果是ViewPager,LifeCycle会有哪些影响?

以FragmentStatePagerAdapter为例,我们首先发现FragmentStatePagerAdapter的构造方法最多支持两个参数:

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

其中behavior默认是BEHAVIOR_SET_USER_VISIBLE_HINT:

默认值,只为了兼容老版本的setUserVisiableByHint()

还有一个值是:BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT

从命名上看,我们可以推测,只有当前Fragment才调用Resume。

这是一个单向选择,如果是BEHAVIOR_SET_USER_VISIBLE_HINT,那么就会和以前一样,还会回调setUserVisibleHint,但是这个方法已经废弃,我觉得可能有两种原因:

1、setUserVisibleHint的周期相对不稳定,所以为了实现懒加载不得不增加初始化判断;
2、onResume更符合页面回到前台的设定。

如果设置的BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT,当前Fragment会回调onResume,被划走的Fragment会回调onPause,但是不会执行setUserVisibleHint。

具体实现我们看一下源码:

@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
     ...
     if (mBehavior == BEHAVIOR_SET_USER_VISIBLE_HINT) {
         fragment.setUserVisibleHint(false);
     }
     ...
     if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
         mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED);
     }
     return fragment;
}

@Override
@SuppressWarnings({"ReferenceEquality", "deprecation"})
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
      ...
      // 不是当前Fragment
      if (fragment != mCurrentPrimaryItem) {
            ...
            if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                ...
                // 周期只到onCreate
                mCurTransaction.setMaxLifecycle(mCurrentPrimaryItem, Lifecycle.State.STARTED);
            } else {
                mCurrentPrimaryItem.setUserVisibleHint(false);
            }
      }
      else{
            ...
            if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
                ...
                 // 周期只到onResume
                mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED);
            } else {
                fragment.setUserVisibleHint(true);
            }
      }
    }

从源码上看都是if判断,所以具体选择选择方式就要看大家怎么选择了。

总结

以上就是今天的笔记,希望对正在研究LifeCycle的朋友有所帮助,如果有表述不清的地方欢迎留言讨论。

上一篇下一篇

猜你喜欢

热点阅读