在做android开发时候,看到一行代码,觉得奇怪,于是就有来了个刨根问底。废话不多说!
在写自己的FragmentPagerAdapter的时候,有下面几行代码:
@Override
public Fragment getItem(int position) {
TabInfo info = mTabs.get(position);
DeskClockFragment f = (DeskClockFragment) Fragment.instantiate(
mContext, info.clss.getName(), info.args);
return f;
}
对于getItem这个方法,大家都很熟悉。从上面的代码可以知道,每一次回调getItem这个函数时候,似乎都会创建一个DeskClockFragment实例,这显然不是一个好的办法,为何,从Adapter的优化方法可以知道,DeskClockFragment这个实例如果能每次都重用不是更好吗!难道是google写的这行代码我们还可以优化吗?答案当然是否定的!要搞清楚这个问题,我们要看看Fragment的这个方法instantiate到底做了些什么。
下面我们来看看instantiate这个方法的定义:
public static Fragment instantiate(Context context, String fname, Bundle args) {
try {
Class<?> clazz = sClassMap.get(fname);
if (clazz == null) {
// Class not found in the cache, see if it's real, and try to add it
clazz = context.getClassLoader().loadClass(fname);
if (!Fragment.class.isAssignableFrom(clazz)) {
throw new InstantiationException("Trying to instantiate a class " + fname
+ " that is not a Fragment", new ClassCastException());
}
sClassMap.put(fname, clazz);
}
Fragment f = (Fragment)clazz.newInstance();
if (args != null) {
args.setClassLoader(f.getClass().getClassLoader());
f.mArguments = args;
}
return f;
} catch (ClassNotFoundException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
} catch (java.lang.InstantiationException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
} catch (IllegalAccessException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
}
}
其实这个方法有google的说明,不过光是看了说明也不太明白!我这里分几步来分析这个方法:
(1)instantiate这个方法是一个static的方法!那就说明,这个方法跟创建的这个对象是没有直接关系的。这个方法是Fragment的这个class的静态方法!
(2)接着我们来看看sClassMap是个什么东西,在Fragment里面有如下代码对sClassMap的定义:
private static final HashMap<String, Class<?>> sClassMap =
new HashMap<String, Class<?>>();
对于sClassMap有两个很特别的修饰:static和final。这说明sClassMap是属于所有Fragment实例的、是用于记录所有加载过的Fragment的。
不过,奇怪的是在Fragment里面sClassMap也只是在instantiate这个方法里面用到! 那就奇怪了,平常我们用一个Fragment时候也没有说要去调用instantiate这个方法呀!其实不然! 在Activity里面,有一个方法引起了我的注意: public View onCreateView(View parent, String name, Context context, AttributeSet attrs),这里我贴出该方法的主要代码:
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
if (!"fragment".equals(name)) {
return onCreateView(name, context, attrs);
}
...
...
if (fragment == null) {
fragment = Fragment.instantiate(this, fname);
fragment.mFromLayout = true;
...
...
}
看到了吗?其实平常我们使用的时候更本不用自己去调用Fragment的方法:instantiate,而是我们的Activity会自己去调用!
(3)接下来我们看看这行代码:Fragment f = (Fragment)clazz.newInstance(); 这个很简单其实就是获取单例的方法!这不用多说。
我要说的已经完了! 大家可以自己去总结一下!