在做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(); 这个很简单其实就是获取单例的方法!这不用多说。
我要说的已经完了! 大家可以自己去总结一下!