1. 程式人生 > >android的Fragment解析(一行程式碼引發的思考)

android的Fragment解析(一行程式碼引發的思考)

在做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();  這個很簡單其實就是獲取單例的方法!這不用多說。

我要說的已經完了!  大家可以自己去總結一下!