之前写过两个项目,都是类似于微信这样的底部四个导航,实现这样的效果方式其实比较多,当时我的做法为viewpager结合fragment
那么我在开发的工程中遇到了哪些问题,并且做了哪些优化呢?
1.由于原生的viewpager自带左右各缓存一个page的特点,每次切换fragment后再切换回来,fragment的onCreateView方法都会被重新执行,在该方法中会进行inflate等相关操作,比较消耗资源,并且inflate后的findViewByID方法也是比较耗时间的。。。个人觉得此处可以有所优化
2.往往初始化完view之后,都会去获取一次数据,这个数据可能是网络的,可能是数据库的,对于数据加载是否可以有所优化,因为内存中存在有数据,但是如果用户当前页的数据比较大,或者当前页的功能比较多,极少有可能切换到下一页的时候,viewpager的缓存效果岂不是不大了?
针对这样的情况,我做的优化工作有哪些?
1.通过既然每次onCreateView都会执行,每次都会inflate相关的布局,那么为什么不把相关的布局view拿出来,当我第一次inflate完以后,之后每次需要使用的时候直接返回就好了嘛,即在baseFragment(这是我Fragment的基类)中定义一个view类型的全局变量rootView,每次执行onCreateView的时候都先判断下rootView是否是空,如果是空的话,那么就去inflate布局,如果不是空的话,直接就将这个rootView对象返回即可。这样就有效避免了每次执行onCreateView方法都会去inflate的情况,类似于这样的判空,在app很多地方都会有。
2.对于数据加载,一般的做法就是我要用的时候才去加载数据,这就是通常说的懒加载,说到“懒”这个字的时候,就想起了单例模式中的两种方式,懒汉式和饿汉式,刚开始的时候,我真的分不清什么是懒汉式,什么是饿汉式,所谓懒汉,即对象很懒,只有当需要使用这个对象的时候,这个对象才会被创建出来,那么相对应的饿汉,就是对象很饥饿,一开始的时候就创建出来以满足需求,以后每次需要使用的时候,直接把这个对象拿过来用即可。回归刚刚的懒加载方式,fragment有一个方法叫做setUserVisibleHint,这个方法有两个特点,每当fragment当前可见或不可见时都会被回调,另外就是这个方法的调用时机早于onCreateView方法,基于这两点认识,我重写了该方法,在方法内判断对调回来的参数isVisibleToUser,如果isVisibleToUser为true,说明可见,那么马上就去加载相关的数据,否则就什么都不做,等待用户切换界面时再去加载数据,这样可以节约一点资源,Android手机的配置虽然高,但是分给每一个app的内存都是有限的,还是能节约就节约,能优化就优化。尤其是如果想做一个好的程序员,发现有的时候用eclipse写代码,会在代码的视图窗口的右侧边栏提示黄色的警报,有可能是因为多余的导包所致,这时也会想很多方法去消除这样的警报,当看到整个类都很干净后才会安心,我想对于很多同行都有类似的强迫症。。。
3.基于第二点的懒加载的认识,以及对于setUserVisibleHint方法的认识,那么之前所做的第一点优化还可以再进一步,既然数据都是在用户可见以后才去加载的,那么为什么初始化view放到用户可见之后再去做呢,要懒,就懒的彻底点,记得之前听一位老程序员说,不会“偷懒”的程序员不是好程序员,所以再一次优化时,直接在onCreateView方法中返回一个空的布局文件即可,每次用户可见时,将真正的布局inflate出来以后添加到这个容器中即可,最后我的setUserVisibleHint方法是这样的:
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
if (null == contentView) {
dialog = ProgressDialog.show(getActivity(), null, null);
contentView = mInflater.inflate(getContentID(), null);
initViews(contentView);
if (null != rootView) {
((ViewGroup) rootView).addView(contentView);
}
}
loadDatas();
}
}
将整个fragment封装成基类的话是这样的:
public abstract class BaseFragment extends Fragment {
public static int DelayTime = 500;
private View rootView, contentView;
private LayoutInflater mInflater = LayoutInflater.from(App.getInstance());
protected Handler mHandler = new Handler(Looper.getMainLooper());
protected ProgressDialog dialog;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (null == rootView) {
rootView = inflater.inflate(R.layout.layout_base_fragment,
container, false);
if (null != contentView) {
((ViewGroup) rootView).addView(contentView);
}
}
return rootView;
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
if (null == contentView) {
dialog = ProgressDialog.show(getActivity(), null, null);
contentView = mInflater.inflate(getContentID(), null);
initViews(contentView);
if (null != rootView) {
((ViewGroup) rootView).addView(contentView);
}
}
loadDatas();
}
}
@Override
public void onDestroy() {
super.onDestroy();
((ViewGroup) rootView).removeView(rootView);
}
public abstract int getContentID();
public abstract void initViews(View contentView);
public abstract void loadDatas();
}