android的Fragment解析(一行程式碼引發的思考)
阿新 • • 發佈:2019-01-27
在做android開發時候,看到一行程式碼,覺得奇怪,於是就有來了個刨根問底。廢話不多說!
在寫自己的FragmentPagerAdapter的時候,有下面幾行程式碼:
對於getItem這個方法,大家都很熟悉。從上面的程式碼可以知道,每一次回撥getItem這個函式時候,似乎都會建立一個DeskClockFragment例項,這顯然不是一個好的辦法,為何,從Adapter的優化方法可以知道,DeskClockFragment這個例項如果能每次都重用不是更好嗎!難道是google寫的這行程式碼我們還可以優化嗎?答案當然是否定的!要搞清楚這個問題,我們要看看Fragment的這個方法instantiate到底做了些什麼。@Override public Fragment getItem(int position) { TabInfo info = mTabs.get(position); DeskClockFragment f = (DeskClockFragment) Fragment.instantiate( mContext, info.clss.getName(), info.args); return f; }
下面我們來看看instantiate這個方法的定義:
其實這個方法有google的說明,不過光是看了說明也不太明白!我這裡分幾步來分析這個方法: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); } }
(1)instantiate這個方法是一個static的方法!那就說明,這個方法跟建立的這個物件是沒有直接關係的。這個方法是Fragment的這個class的靜態方法!
(2)接著我們來看看sClassMap是個什麼東西,在Fragment裡面有如下程式碼對sClassMap的定義:
對於sClassMap有兩個很特別的修飾:static和final。這說明sClassMap是屬於所有Fragment例項的、是用於記錄所有載入過的Fragment的。private static final HashMap<String, Class<?>> sClassMap = new HashMap<String, Class<?>>();
不過,奇怪的是在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(); 這個很簡單其實就是獲取單例的方法!這不用多說。
我要說的已經完了! 大家可以自己去總結一下!