综合以上两个可见和不可见状态,能够感知的生命周期只有onResume和onPasue,外加一个setUserVisibleHint方法,那么懒加载也就是在这三个方法中分发正确的事件,可以将第一次加载,懒加载,中断,这三个操作抽出成为方法,由子类实现,父类负责分发。
上面的两种情况分析,看上去非常的简单,实际上懒加载也就是完成了以上的工作,但是,实际代码中,会有坑,onResume和onPause。
先说onResume需要考虑的问题:
假设,现在有三个tab一个viewpager,每个tab对应着一个fragment。当tab1切换到tab2时,由于viewpager中的setOffscreenPageLimit默认值为1,也就意味着tab3此时会走onResume的生命周期;
再比如:Activity1中有多个fragment,此时有Activity1跳转到Activity2,那么Activity1中会缓存多个Fragment,当由Activity2返回Activity1时,会走fragment的onResume生命周期;
那么onResume要做如下处理:
伪代码:
public void onResume() {
super.onResume();
//这个if是为了解决tab来回切换的问题,当切换到tab2时,tab3已经执行onResume,但是view还没创建
//所以需要增加一层判断,当第一次可见时,再去分发事件
if(如果第一次可见){
//由于activity切换时,fragment不会走setUserVisibleHint方法,所以也需要增加一层判断
if (没有hidden && 当前状态不可见 && getUserVisibleHint()){
//分发可见事件
}
}
}
解决完onResume,继续解决onPause
onPause不需要onResume里的第一层判断,因为只要执行onPause那说明已经执行过了onCreate以及onResume。同样,当Activity1跳转到Activity2时,1里面的所有fragment都会执行onPause,那么只需要处理由可见变为不可见状态的fragment,由可见变为不可见时,需要分发事件,处理中断操作。
伪代码:
public void onPause() {
super.onPause();
//这里也就是说 当前可见的Fragment 需要分发中断事件
if (当前可见 && getUserVisibleHint()){
//分发不可见事件
}
}
bug出现情况,假设当前有三个tab,每个tab对应一个fragment,tab2对应的fragment里嵌套了一层viewpager,当tab1加载时,tab2对应的fragment里面嵌套的viewpager里的第一个fragment也会执行onResume。这个情况是非常容易被忽略掉的,处理的逻辑很简单,在分发事件时,首先判断父Fragment是否可见,只有可见情况下才会继续分发。
使用时,对应的Fragment继承该父类,子类主动实现firstLoad、lazyLoad、interrupt 三个方法即可。如果有嵌套,嵌套的Fragment也继承该父类即可
public abstract class LazyFragment extends Fragment {
protected View mRootView = null; //view复用
protected boolean isCreateView = false; //是否创建
protected boolean currentVisibleState = false; //当前可见状态
protected boolean isFirstVisible = true; //是否第一次可见
protected abstract int setLayoutId();
//初始化view
protected abstract void initView(View view);
//第一次加载 留给子类实现
protected void firstLoad(){
}
//切换之后的懒加载 留给子类实现
protected void lazyLoad() {
}
//切换后中断加载 留给子类实现
protected void interrupt() {
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//父view复用
if (mRootView == null) {
mRootView = inflater.inflate(setLayoutId(), container, false);
}
initView(mRootView);
//已经创建view
isCreateView = true;
//第一次加载
if (!isHidden() && getUserVisibleHint()) {
dispatchLazyLoadOrInterrupt(true);
}
return mRootView;
}
@Override
public void onResume() {
super.onResume();
/**
* 已经加载过的 fragment 会遇到的情况
* fragment 所在的 activity 跳转到其他 activity
* 在 activity 返回时 需要加载
* 只加载可见 fragment
*/
if(!isFirstVisible){
if (!isHidden() && !currentVisibleState && getUserVisibleHint()){
dispatchLazyLoadOrInterrupt(true);
}
}
}
@Override
public void onPause() {
super.onPause();
//是否需要分发中断事件
if (currentVisibleState && getUserVisibleHint()){
dispatchLazyLoadOrInterrupt(false);
}
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isCreateView) { // createview 之后才可以加载数据
if (!currentVisibleState && isVisibleToUser) { //不可见 -> 可见
dispatchLazyLoadOrInterrupt(true);
} else if (currentVisibleState && !isVisibleToUser) { // 可见 -> 不可见
dispatchLazyLoadOrInterrupt(false);
}
}
}
/**
* 处理fragment切换时 懒加载 和 中断
* 可见 -> 不可见 = 中断操作,网络请求等
* 不可见 -> 可见 = 懒加载
* @param isVisible
*/
private void dispatchLazyLoadOrInterrupt(boolean isVisible) {
// ParentFragment不可见 则不处理
// 处理fragment里嵌套fragment的情况
if (isVisible && isParentInVisible()) {
return;
}
if (currentVisibleState == isVisible) {
return;
}
// save当前可见性
currentVisibleState = isVisible;
if (isVisible) {
/**
* fragment 第一次加载
* 主要处理这种情况:
* fragment第一次加载网络请求的数据缓存到本地,之后加载去读取本地
* 那么,第一次网络请求通过 firstLoad,之后在 lazyLoad 中读取本都缓存
*/
if (isFirstVisible){
isFirstVisible = false;
firstLoad();
}
lazyLoad();
dispatchChildLazyLoadOrInterrupt(isVisible);
} else {
interrupt();
dispatchChildLazyLoadOrInterrupt(isVisible);
}
}
/**
* 当 fragment 中嵌套 fragment 时要处理 可见的childfragment
* @param isVisible
*/
public void dispatchChildLazyLoadOrInterrupt(boolean isVisible){
FragmentManager fm = getChildFragmentManager();
List fragmentList = fm.getFragments();
for (Fragment item : fragmentList){
if (item instanceof LazyFragment && !isHidden() && item.getUserVisibleHint()){
((LazyFragment) item).dispatchLazyLoadOrInterrupt(isVisible);
}
}
}
/**
* 当 fragment 通过 FragmentTransaction 的 show/hide 方法
* 改变 Fragment 可见性时,在这里处理调度逻辑
* @param hidden
*/
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
if (hidden){
dispatchLazyLoadOrInterrupt(false);
}else {
dispatchLazyLoadOrInterrupt(true);
}
}
/**
* ParentFragment 是不是不可见
*
* @return true -> 不可见 / false -> 可见
*/
private boolean isParentInVisible() {
Fragment parent = getParentFragment();
if (parent instanceof LazyFragment) {
LazyFragment temp = (LazyFragment) parent;
return !temp.getCurrentVisibleState();
}
return false;
}
/**
* 获取 fragment 当前的可见性
* @return
*/
private boolean getCurrentVisibleState() {
return currentVisibleState;
}
}