现在很多app的架构,基本都是viewpager+fragment的,复杂一点的可能还含有嵌套,例如我们公司的app:
可以看到采用的是:底部按钮+[viewpager+fragment[viewpager+fragment]]嵌套的模式
简单来讲如图:
布局如下:
main_activity.xml
fragment_a.xml
当我们深入viewpager加载需要一个adapter
android提供的有FragmentPagerAdapter/FragmentStatePagerAdapter
区别在于前者在没有在destroyItem中调用实务的detach方法:
FragmentPagerAdapter:
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
mCurTransaction.detach((Fragment)object);
}
FragmentStatePagerAdapter:
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment) object;
//复制数组保存的fragment为null,不采用remove是因为在滑动的时候需要创建(instantiateItem:mFragments.set(position, fragment)),这里节省数组的插入位移数据时间来提升效率
mFragments.set(position, null);
//移除
mCurTransaction.remove(fragment);
}
这里我们主要结合FragmentStatePagerAdapter来优化viewpager下的数据加载
简单分析FragmentStatePagerAdapter
入口viewpage.setAdapter(xxx)
public void setAdapter(@Nullable PagerAdapter adapter) {
if (mAdapter != null) {
//recycler adapter,将设置的adapter数据全清空
......
}
//将mAdpter复制
mAdapter = adapter;
if (mAdapter != null) {
//初始化adapter的一些必要参数
.....
if (mRestoredCurItem >= 0) {
//还原adapter数据状态
...
} else if (!wasFirstLayout) {
//注意这里
populate();
} else {
requestLayout();
}
}
}
观察populate函数,该函数是执行fragment生命周期的关键函数
void populate(int newCurrentItem) {
//FragmentStatePagerAdapter的startUpdate
mAdapter.startUpdate(this);
if (curItem == null && N > 0) {
curItem = addNewItem(mCurItem, curIndex);
}
//执行的是,返回一个fragment对象
mAdapter.instantiateItem(this, position)
//当前的fragment被选中
mAdapter.setPrimaryItem(this, mCurItem, curItem.object);
//完成
mAdapter.finishUpdate(this);
}
具体可知,viewpager在调用setAdapter(FragmentStatePagerAdapter fpg)的时候,
FragmentStatePagerAdapter 会依次执行
startUpdate--->instantiateItem()-->setPrimaryItem-->finishUpdate方法,那么再看下
FragmentStatePagerAdapter 的源码
FragmentStatePagerAdapter
startUpdate是个空实现
instantiateItem
@Override
public Object instantiateItem(ViewGroup container, int position) {
//取出缓存的Fragment
if (mFragments.size() > position) {
Fragment f = mFragments.get(position);
if (f != null) {
return f;
}
}
//创建事务
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
Fragment fragment = getItem(position);
//更新保存的状态
if (mSavedState.size() > position) {
Fragment.SavedState fss = mSavedState.get(position);
if (fss != null) {
fragment.setInitialSavedState(fss);
}
}
while (mFragments.size() <= position) {
mFragments.add(null);
}
fragment.setMenuVisibility(false);
//调用fragment setUserVisibleHint
fragment.setUserVisibleHint(false);
//更新该fragment,这里和后面set(position,null)相对应,节省性能
mFragments.set(position, fragment);
//执行事务的add方法,将fragment加入到事务中
mCurTransaction.add(container.getId(), fragment);
return fragment;
}
setPrimaryItem
@Override
@SuppressWarnings("ReferenceEquality")
public void setPrimaryItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment)object;
//当缓存的CurrentFragment不是选中的fragment执行
if (fragment != mCurrentPrimaryItem) {
if (mCurrentPrimaryItem != null) {
mCurrentPrimaryItem.setMenuVisibility(false);
//调用setUserVisibleHint
mCurrentPrimaryItem.setUserVisibleHint(false);
}
if (fragment != null) {
fragment.setMenuVisibility(true);
//调用setUserVisibleHint
fragment.setUserVisibleHint(true);
}
//更新缓存当前fragment
mCurrentPrimaryItem = fragment;
}
}
destroyItem
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment) object;
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
while (mSavedState.size() <= position) {
mSavedState.add(null);
}
mSavedState.set(position, fragment.isAdded()
? mFragmentManager.saveFragmentInstanceState(fragment) : null);
//set null,对应上面的add position
mFragments.set(position, null);
//从事务中已移除
mCurTransaction.remove(fragment);
}
可以看到,这里完成了fragment的生命周期,但是由于fragment的生命周期是又FragmentManager的事务FragmentTransaction来管理的,所以,在没有调用commit之前是不会进行生命周期的
这里finishUpdate函数调用的commit
finishUpdate
@Override
public void finishUpdate(ViewGroup container) {
if (mCurTransaction != null) {
mCurTransaction.commitNowAllowingStateLoss();
mCurTransaction = null;
}
}
所以,在所有的生命周期中,setUserVisibleHint将会最先执行。
懒加载的本质是一种算法,因为viewpager中的预加载是无法避免的(内部缓存了mFragments来存储fragment),在populate中两个for循环中缓存了左右两边数据
for (int pos = mCurItem - 1; pos >= 0; pos--) {
//leftItem
ii(left) = mItems.get(itemIndex) ;
}
for (int pos = mCurItem - 1; pos >= 0; pos--) {
//leftItem
ii(right)= mItems.get(itemIndex) ;
}
所以,懒加载就是在数据上做优化,可见时加载数据,不可见时停止加载数据
LazyFragment
/**
* @author chenke
* @create 2021/2/25
* @Describe
*/
public abstract class LazyFragment extends Fragment {
private final String TAG = "lazy_fragment";
private View mRootView;
private boolean viewCreated = false;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if (mRootView == null) {
mRootView = inflater.inflate(getLayout(), container, false);
}
initView(mRootView);
viewCreated = true;
Log.i(TAG, getClass().getSimpleName() + "====>onCreateView");
if (getUserVisibleHint()) {
//由于onCreateView,在执行之前setUserVisibleHint已经执行,所以这里手动分发一次可见状态为true
setUserVisibleHint(true);
}
return mRootView;
}
public abstract int getLayout();
public abstract void initView(View view);
/**
*setUserVisibleHint会优先于所有生命周期的执行,
*所以这里增加标志位viewCreated,视图创建了才执行函数
*/
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
Log.i(TAG, getClass().getSimpleName() + "====>setUserVisibleHint");
if (viewCreated) {
if (isVisibleToUser) {
//选中的时候可见,分发
dispatchUserVisibleStatus(true);
} else if (!isVisibleToUser) {
//分发不可见
dispatchUserVisibleStatus(false);
}
}
}
public void dispatchUserVisibleStatus(boolean isUserVisibleStatus) {
if (isUserVisibleStatus) {
onStartLoad();
} else {
onStopLoad();
}
}
@Override
public void onResume() {
super.onResume();
Log.i(TAG, getClass().getSimpleName() + "===>onResume");
}
@Override
public void onPause() {
super.onPause();
Log.i(TAG, getClass().getSimpleName() + "===>onPause");
}
@Override
public void onDetach() {
super.onDetach();
Log.i(TAG, getClass().getSimpleName() + "===>onDetach");
}
@Override
public void onDestroyView() {
super.onDestroyView();
Log.i(TAG, getClass().getSimpleName() + "===>onDestroyView");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, getClass().getSimpleName() + "===>onDestroy");
}
/**
* 子类重写该方法来实现开始加载数据
*/
public void onStartLoad() {
Log.i(TAG, getClass().getSimpleName() + "====>开始加载数据onStartLoad");
}
/**
* 子类重写该方法来实现暂停数据加载
*/
public void onStopLoad() {
Log.i(TAG, getClass().getSimpleName() + "====>停止加载数据onStopLoad");
}
}
如图:
日志输出情况:
可以看到符合预期,那么这里是不是结束了,显然不是,因为在viewpage+fragment中如果存在嵌套的情况下,他在滑动到,嵌套的fragment,那么fragment的子类都会执行
所以需要加上在嵌套的时候,父类去控制子类的分发
public void dispatchUserVisibleStatus(boolean isUserVisibleStatus) {
if (isUserVisibleStatus) {
onStartLoad();
} else {
onStopLoad();
}
//在嵌套模式下,让子类的fragment进行分发
FragmentManager fm = getChildFragmentManager();
List fragments = fm.getFragments();
if (fragments.size() > 0) {
for (Fragment fragment : fragments) {
if (fragment instanceof LazyFragment) {
//当前的fragment状态为可见时才分发
if (fragment.getUserVisibleHint()) {
((LazyFragment) fragment).dispatchUserVisibleStatus(true);
}
}
}
}
}
但,此时的懒加载还不完整,因为,当如果我们从fragment跳转到一个activity过后,由于viewpager没有做改变,所以不会触发setUserVisibleHint函数,我们需要在onResume和onPause来执行数据的暂停和加载
/**
*记录当前fragment在执行数据加载/停止加载之前的状态
*/
boolean currentVisibleStatus=false
@Override
public void onResume() {
super.onResume();
Log.i(TAG, getClass().getSimpleName() + "===>onResume");
if (getUserVisibleHint() && !currentVisibleStatus) {
//getUserVisibleHint() 不会执行,始终为true
//不可见-->可见,加载数据
dispatchUserVisibleStatus(true);
}
}
@Override
public void onPause() {
super.onPause();
Log.i(TAG, getClass().getSimpleName() + "===>onPause");
if (getUserVisibleHint() && currentVisibleStatus) {
//可见-->不可见,停止加载
dispatchUserVisibleStatus(false);
}
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
Log.i(TAG, getClass().getSimpleName() + "====>setUserVisibleHint");
if (viewCreated) {
if (!currentVisibleStatus && isVisibleToUser) {
//之前状态不可见-->当前状态可见,加载数据
dispatchUserVisibleStatus(true);
} else if (currentVisibleStatus && !isVisibleToUser) {
//之前状态可见-->当前状态不可见,暂停数据加载
dispatchUserVisibleStatus(false);
}
}
}
完整LazyFragmet
/**
* @author chenke
* @create 2021/2/25
* @Describe
*/
public abstract class LazyFragment extends Fragment {
private final String TAG = "lazy_fragment";
private View mRootView;
private boolean viewCreated = false;
private boolean currentVisibleStatus = false;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if (mRootView == null) {
mRootView = inflater.inflate(getLayout(), container, false);
}
initView(mRootView);
viewCreated = true;
Log.i(TAG, getClass().getSimpleName() + "====>onCreateView");
if (getUserVisibleHint()) {
setUserVisibleHint(true);
}
return mRootView;
}
public abstract int getLayout();
public abstract void initView(View view);
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
Log.i(TAG, getClass().getSimpleName() + "====>setUserVisibleHint");
if (viewCreated) {
if (!currentVisibleStatus && isVisibleToUser) {
dispatchUserVisibleStatus(true);
} else if (currentVisibleStatus && !isVisibleToUser) {
dispatchUserVisibleStatus(false);
}
}
}
public void dispatchUserVisibleStatus(boolean isUserVisibleStatus) {
currentVisibleStatus = isUserVisibleStatus;
if (isUserVisibleStatus) {
onStartLoad();
} else {
onStopLoad();
}
//在嵌套模式下,让子类的fragment进行分发
FragmentManager fm = getChildFragmentManager();
List fragments = fm.getFragments();
if (fragments.size() > 0) {
for (Fragment fragment : fragments) {
if (fragment instanceof LazyFragment) {
if (fragment.getUserVisibleHint()) {
((LazyFragment) fragment).dispatchUserVisibleStatus(true);
}
}
}
}
}
@Override
public void onResume() {
super.onResume();
Log.i(TAG, getClass().getSimpleName() + "===>onResume");
if (getUserVisibleHint() && !currentVisibleStatus) {
dispatchUserVisibleStatus(true);
}
}
@Override
public void onPause() {
super.onPause();
Log.i(TAG, getClass().getSimpleName() + "===>onPause");
if (getUserVisibleHint() && currentVisibleStatus) {
dispatchUserVisibleStatus(false);
}
}
@Override
public void onDetach() {
super.onDetach();
Log.i(TAG, getClass().getSimpleName() + "===>onDetach");
}
@Override
public void onDestroyView() {
super.onDestroyView();
Log.i(TAG, getClass().getSimpleName() + "===>onDestroyView");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, getClass().getSimpleName() + "===>onDestroy");
}
/**
* 子类重写该方法来实现开始加载数据
*/
public void onStartLoad() {
Log.i(TAG, getClass().getSimpleName() + "====>开始加载数据onStartLoad");
}
/**
* 子类重写该方法来实现暂停数据加载
*/
public void onStopLoad() {
Log.i(TAG, getClass().getSimpleName() + "====>停止加载数据onStopLoad");
}
}
总结:
- viewpager+fragment的结构下,子fragment集合会依次执行startUpdate--->instantiateItem()-->setPrimaryItem-->finishUpdate
- 由于fragment的由FragmentManager的事务FragmentTransaction来管理,而viewpager是在finishUpdate函数后才提交事务,所以setUserVisibleHinit会最先执行。
- FragmentStatePagerAdapter和FragmentPagerAdapter区别在于前者调用事务的remove函数,而后者这是detach,所有FragmentStatePagerAdapter需要几率fragment的状态,更消耗内存
- viewpager内部存储的mFragments数组是通过置空来优化list的性能(add和remove会频繁的移动list的指针)
以上是分析support包下的viewpager+fragment的懒加载,由于事务在是setUserVisibleHint之后执行,让viewpager+fragment的这种结构的数据,在可见情况下无法做到有效的控制,所以AndroidX下的Fragment新增的maxLifecycle来控制最大生命周期,这也是setUserVisibleHint被废弃的原因,下面我会再介绍在AndroidX下如何通过最大生命周期来优化懒加载。