赞
踩
当被问到上述问题时,很多人可能首先会想到借助setUserVisiblity
实现
如下,当Fragment可见时调用 onVisible
从而实现异步加载
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (getUserVisibleHint()) {
isVisible = true;
onVisible();
} else {
isVisible = false;
onInVisible();
}
}
放在两年前,这个答案是OK的,但是2021年的今天还这么回答可能就不过关了。
https://android-review.googlesource.com/c/platform/frameworks/support/+/945776
AndroidX 自 1.1.0-alpha07 起, 为 FragmentTransaction
增加了新的方法 setMaxLifeCycle
, 官方建议开发者以此取代setUserVisibleHint,这将带来如下好处:
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 使用方法很简单,接下来通过梳理源码了解一下实现原理(基于1.3.0-rc01),即使面试官追问其原理你也能沉着应对。
我们知道 FramgentTransition 对 Fragment 的所有操作都将转换为一个Op,针对setMaxLifecycle也同样增加了一个新的Op – OP_SET_MAX_LIFECYCLE
, 专门用来设置生命周期的上限。
@NonNull
public FragmentTransaction setMaxLifecycle(@NonNull Fragment fragment,
@NonNull Lifecycle.State state) {
addOp(new Op(OP_SET_MAX_LIFECYCLE, fragment, state));
return this;
}
当 FramgentTransition 对 Frament 添加了 OP_SET_MAX_LIFECYCLE 后,在实现类 BackStackRecord 中, FragmentManager 会遍历 Transaction 的 Op 列表
void executeOps() {
final int numOps = mOps.size();
for (int opNum = 0; opNum < numOps; opNum++) {
final Op op = mOps.get(opNum);
final Fragment f = op.mFragment;
//...
switch (op.mCmd) {
//...
// 新引入的这个Op类型, 在这里会给这个Fragment设置允许的生命周期上限
case OP_SET_MAX_LIFECYCLE:
mManager.setMaxLifecycle(f, op.mCurrentMaxState);
break;
//...
}
}
当遇到 OP_SET_MAX_LIFECYCLE 时,通过调用 FragmentManager 的 setMaxLifeCycle 方法设置 fragment 的 mMaxState,以标记其生命周期上限
void setMaxLifecycle(@NonNull Fragment f, @NonNull Lifecycle.State state) {
//...
f.mMaxState = state;
}
FragmentManager 通过 FragmentStateManager 推进 Fragment 的生命周期。 推进过程中根据 mMaxState 对生命周期
值得一提的是,FragmentStateManager 是 1.3.0-alpha08 之后新增的类,将原来和 State 相关的逻辑从FragmentManager 抽离了出来, 降低了与 Fragment 的耦合, 职责更加单一。
看一下在 FragmentStateManager 中具体是如何推进 Fragment 生命周期的:
void moveToExpectedState() { try { ... // 循环计算声明周期是否可以推进 while ((newState = computeExpectedState()) != mFragment.mState) { if (newState > mFragment.mState) { // 生命周期向前推进 int nextStep = mFragment.mState + 1; //... switch (nextStep) { //... case Fragment.ACTIVITY_CREATED: //... case Fragment.STARTED: start(); break; //... case Fragment.RESUMED: resume(); break; } } else { // 如果应有的生命周期小于当前, 后退 int nextStep = mFragment.mState - 1; //... switch (nextStep) { // 与上面的switch类似 //... } } } ... } ... }
int computeExpectedState() { // 其他计算expected state的逻辑, 算出maxState //... // mMaxState 对生命周期做出限制 switch (mFragment.mMaxState) { case RESUMED: break; case STARTED: maxState = Math.min(maxState, Fragment.STARTED); break; case CREATED: maxState = Math.min(maxState, Fragment.CREATED); break; default: maxState = Math.min(maxState, Fragment.INITIALIZING); } // 其他计算expected state的逻辑, 算出 maxState // ... return maxState; }
整体流程图如下
除了使用默认的 BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT,我们甚至可以在自定义 Adapter 的instantiateItem 中为将 Fragment的 MaxLifecycle 设置为 CREATED
, 这样可以让 Fragment 只走到onCreate 从而延迟更多操作,比如在 onCreateView 中的 inflate 以及 onViewCreated 中的一些操作。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。