性能优化系列之fragment的懒加载

来源:互联网 发布:各个国家顶级域名 编辑:程序博客网 时间:2024/05/18 01:54
     自从实习以来一直在忙着赶项目进度,没有时间(懒)总结。现在终于有空总结一下这两个多月的收获了。从性能优化中的fragment懒加载开始梳理吧。
     项目中使用ViewPager+Fragment,拿练手项目xMusic来说吧,在MainActivity中有两个fragment,一个是本地播放页面,一个是“发现”页面,“发现”页面包含广告轮播图和推荐歌单,图片资源比较多。正常打开app时两个fragment都会进行网络请求加载资源。刚打开app的时候,内存占用能上到88-90m。对于很大一部分只听本地歌曲的用户来说,加载“发现”页面不仅占用内存,还消耗网络资源。
     ViewPager中有一个方法  setOffscreenPageLimit(int limit)  可以设置预加载的页面数量。通过源码我们可以知道,这个方法设置的limit最终都不会小于1。因此对于我们这种情况,setOffscreenPageLimit(int limit)是无效的。

public void setOffscreenPageLimit(int limit) {
    if (limit < DEFAULT_OFFSCREEN_PAGES) {
        Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to "
                + DEFAULT_OFFSCREEN_PAGES);
        limit = DEFAULT_OFFSCREEN_PAGES;
    }
    if (limit != mOffscreenPageLimit) {
        mOffscreenPageLimit = limit;
        populate();
    }
}
    
     这时候就需要使用fragment的懒加载机制,在用户手动滑入“发现”页面时fragment才进行加载,节省资源。
     fragment中提供一个方法 setUserVisibleHint,这个方法会在fragment对用户可见/不可见时回调,但是它并不在fragment的生命周期中,因此无法保证和生命周期方法的调用顺序。
    
     创建一个抽象类BaseLazyFragment,然后定义几个标识位
private boolean isFirstVisible = true;
private boolean isFirstInvisible = true;

private boolean isViewCreated;
private boolean isUIVisible;
   
     重写方法onViewCreated,并在其中进行加载操作(最终是否加载还取决于当前fragment是否可见,这将在lazyLoad中进行判断):
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    isViewCreated = true;
    lazyLoad(); // 执行懒加载,因为无法确定setUserVisibleHint和onViewCreated哪个方法先执行,因此两个方法里面都需要调用lazyLoad
}
 
     然后重写方法:
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser) {
        isUIVisible = true; //当前fragment可见
        if (isFirstVisible) {
            //如果是第一次可见,则进行懒加载
            isFirstVisible = false;
            lazyLoad();
        } else {
            //不是第一次可见,则调用onUserVisible()
            onUserVisible();
        }
    } else {
        isUIVisible = false;
        if (isFirstInvisible) {
            isFirstInvisible = false;
            //第一次不可见
            onFirstUserInvisible();
        } else {
            //非第一次不可见
            onUserInvisible();
        }
    }
}

     添加lazyLoad方法,执行懒加载:

private void lazyLoad() {
    if (isViewCreated && isUIVisible) { //需要进行双重判断,避免因为setUserVisibleHint先于onViewCreaetd调用时,出现空指针
        onFirstUserVisible();  //进行初次可见时的加载
    }
}
     添加几个抽象方法:
protected abstract void onFirstUserVisible();

protected abstract void onUserVisible();

protected abstract void onFirstUserInvisible();

protected abstract void onUserInvisible();

    
     Fragment的懒加载大概就是这样,比较简单。添加几个标识位判断是否是第一次可见/不可见、onViewCreated是否执行完(必要,否则setUserVisibleHint在onViewCreated执行完前回调,然后进行懒加载的话可能会导致空指针)。在加入懒加载之后,打开app时的内存占用能降到70m左右,下降了近20%,对于一些性能较差的用户还是比较关键的。

    不过懒加载也有坏处就是用户第一次进入页面时需要短暂等待加载。因此是否需要懒加载得根据产品定位、目标人群等来决定,这就是产品经理需要考虑的问题了蛤蛤。

完整的BaseLazyFragment代码如下:


import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public abstract class BaseLazyFragment extends Fragment {
    private boolean isFirstVisible = true;
    private boolean isFirstInvisible = true;

    private boolean isViewCreated;
    private boolean isUIVisible;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        Log.i("LazyLoad", getClass().getName() + "onCreateView");
        if (getLayoutId() != 0) {
            return inflater.inflate(getLayoutId(), container, false);
        } else {
            return super.onCreateView(inflater, container, savedInstanceState);
        }
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        isViewCreated = true;
        lazyLoad(); // 执行懒加载,因为无法确定setUserVisibleHint和onViewCreated哪个方法先执行,因此两个方法里面都需要调用lazyLoad
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser) {
            isUIVisible = true; //当前fragment可见
            if (isFirstVisible) {
                //如果是第一次可见,则进行懒加载
                isFirstVisible = false;
                lazyLoad();
            } else {
                //不是第一次可见,则调用onUserVisible()
                onUserVisible();
            }
        } else {
            isUIVisible = false;
            if (isFirstInvisible) {
                isFirstInvisible = false;
                //第一次不可见
                onFirstUserInvisible();
            } else {
                //非第一次不可见
                onUserInvisible();
            }
        }
    }

    private void lazyLoad() {
        if (isViewCreated && isUIVisible) { //需要进行双重判断,避免因为setUserVisibleHint先于onViewCreaetd调用时,出现空指针
            initViewsAndEvents();
            onFirstUserVisible();  //进行初次可见时的加载
        }
    }

    protected abstract int getLayoutId();

    protected abstract void initViewsAndEvents();

    protected abstract void onFirstUserVisible();

    protected abstract void onUserVisible();

    protected abstract void onFirstUserInvisible();

    protected abstract void onUserInvisible();

}