LifeCycle在Fragment中的使用

前言

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

原文作者博客:https://juejin.im/post/5e085dafe51d45580769a1eb

正文

对于懒加载的实现新思路大家可以详细阅读原文,我这里只是一个学习总结的笔记。首先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的朋友有所帮助,如果有表述不清的地方欢迎留言讨论。

是的,使用`lifecycle`库可以更方便地管理`fragment`的生命周期。通过`lifecycle`库,我们可以将`fragment`的生命周期与宿主`Activity`的生命周期进行绑定,这样就可以在`Activity`的生命周期发生变化时,自动调用`fragment`的对应生命周期方法,从而更好地管理`fragment`的状态。 具体来说,我们可以通过以下步骤来使用`lifecycle`库进行`fragment`的生命周期管理: 1. 在`build.gradle`文件添加`lifecycle`库的依赖: ``` implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' ``` 2. 在`fragment`实现`LifecycleOwner`接口,并将`getLifecycle()`方法返回`Lifecycle`对象: ``` public class MyFragment extends Fragment implements LifecycleOwner { private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this); @NonNull @Override public Lifecycle getLifecycle() { return mLifecycleRegistry; } // ... } ``` 3. 在`fragment`使用`LifecycleRegistry`对象来管理生命周期状态的变化: ``` public class MyFragment extends Fragment implements LifecycleOwner { private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE); } @Override public void onStart() { super.onStart(); mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START); } @Override public void onResume() { super.onResume(); mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME); } // ... @Override public void onDestroy() { super.onDestroy(); mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY); } } ``` 通过这样的方式,我们就可以更方便地管理`fragment`的生命周期,避免出现一些生命周期相关的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值