LifeCycle在Fragment中的使用
前言
今天在浏览技术大牛的公众号文章,看到一篇文章讲解了如何使用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的朋友有所帮助,如果有表述不清的地方欢迎留言讨论。