Fragment为什么须要无参构造方法
日前在項目代碼里遇到偷懶使用重寫Fragment帶參構造方法來傳參的做法,頓生好奇,繼承android.support.v4.app.Fragment而又不寫無參構造方法不是會出現lint錯誤編譯不通過的咩?仔細追究,原來是這貨被加了@SuppressLint("ValidFragment")從而屏蔽了錯誤。(個人非常不建議使用SuppressLint來屏蔽錯誤,盡管編譯階段通過了,運行時錯誤卻越加恐怖!)
廢話不多說,我們回歸正題來看為什么Fragment必須要使用無參的構造方法。為什么?因為不這么干的話咱的app就會崩崩崩唄(咖喱給給~)。
首先,讓我崩給你看~
將app定位至包含此非法(暫且叫它非法)的Fragment的頁面,此時將app切至后臺,等待app被系統回收,為了加快回收,你可以去玩半個小時比較耗內存的游戲等等,顯而易見,這種等待十分愚蠢且不可預知,所以我們可以用借助開發工具立即觸發app的被回收,這里我使用Android Studio來做。點擊下圖中箭頭標注的小叉,你的app就被干掉了。(一定要是debug版本的才會在Android Monitor中出現哦)
接下來我們從歷史任務中打開我們的app,哦哦
這下我們終于如愿以償地看到的崩潰的異常堆棧信息:
Caused by: android.support.v4.app.Fragment$InstantiationException: Unable to instantiate fragment com.tuanche.app.fragment.ChooseCarStyleOnlineRetailersFragment: make sure class name exists, is public, and has an empty constructor that is public at android.support.v4.app.Fragment.instantiate(Fragment.java:413) at android.support.v4.app.FragmentState.instantiate(Fragment.java:97) at android.support.v4.app.FragmentManagerImpl.restoreAllState(FragmentManager.java:1801) at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:213) at com.zhqnghq.app.activity.BaseFragmentActivity.onCreate(BaseFragmentActivity.java:36) at com.zhanghq.app.activity.SecondActivity.onCreate(SecondActivity.java:97)
?
忽略最后兩行,我們從倒數第三行開始看,打開FragmentActivity的onCreate方法,我們可以看到這樣的代碼:
if(savedInstanceState != null) {Parcelable p = savedInstanceState.getParcelable("android:support:fragments");this.mFragments.restoreAllState(p, nc != null?nc.fragments:null); }
?
因為是被kill后恢復,savedInstanceState不等于空,于是我們進入了if代碼塊,再往里走看this.mFragments.restoreAllState做了些什么我們關心的,再次挑出關鍵部分來看:
for(i = 0; i < fms.mActive.length; ++i) {FragmentState var8 = fms.mActive[i];if(var8 != null) {Fragment var9 = var8.instantiate(this.mActivity, this.mParent);if(DEBUG) {Log.v("FragmentManager", "restoreAllState: active #" + i + ": " + var9);}this.mActive.add(var9);var8.mInstance = null;} else {this.mActive.add((Object)null);if(this.mAvailIndices == null) {this.mAvailIndices = new ArrayList();}if(DEBUG) {Log.v("FragmentManager", "restoreAllState: avail #" + i);}this.mAvailIndices.add(Integer.valueOf(i));} }
?
Fragment var9 = var8.instantiate(this.mActivity, this.mParent);這句代碼是我們關注的重點,去看看它做了些什么:
if(this.mInstance != null) {return this.mInstance; } else {if(this.mArguments != null) {this.mArguments.setClassLoader(activity.getClassLoader());}this.mInstance = Fragment.instantiate(activity, this.mClassName, this.mArguments);if(this.mSavedFragmentState != null) {this.mSavedFragmentState.setClassLoader(activity.getClassLoader());this.mInstance.mSavedFragmentState = this.mSavedFragmentState;} ...
?
我滴個神啊,終于看到幾行可以稍微看懂的代碼了,啦啦啊~Fragment.instantiate()是在從字面意思就可以看出是要生成一個新的Fragment,不信我們可以點進去看:
public static Fragment instantiate(Context context, String fname, Bundle args) {try {Class e = (Class)sClassMap.get(fname);if(e == null) {e = context.getClassLoader().loadClass(fname);sClassMap.put(fname, e);}Fragment f = (Fragment)e.newInstance();if(args != null) {args.setClassLoader(f.getClass().getClassLoader());f.mArguments = args;}return f;} catch (ClassNotFoundException var5) {throw new Fragment.InstantiationException("Unable to instantiate fragment " + fname + ": make sure class name exists, is public, and has an" + " empty constructor that is public", var5);} catch (java.lang.InstantiationException var6) {throw new Fragment.InstantiationException("Unable to instantiate fragment " + fname + ": make sure class name exists, is public, and has an" + " empty constructor that is public", var6);} catch (IllegalAccessException var7) {throw new Fragment.InstantiationException("Unable to instantiate fragment " + fname + ": make sure class name exists, is public, and has an" + " empty constructor that is public", var7);} }
?
忽略冗長的catch代碼,可以看到,instantiate方法顯示想法設法構造了一個Class對象,然后調用Class::newInstance()方法構造了一個全新的Fragment對象,最后還原了Fragment的Argument,至此Fragment的恢復大功告成。我們回過頭看來Class::newInstance()的方法注釋(良好的注釋很重要啊~):
/** * Returns a new instance of the class represented by this {@code Class}, * created by invoking the default (that is, zero-argument) constructor. If * there is no such constructor, or if the creation fails (either because of * a lack of available memory or because an exception is thrown by the * constructor), an {@code InstantiationException} is thrown. If the default * constructor exists but is not accessible from the context where this * method is invoked, an {@code IllegalAccessException} is thrown. * * @throws IllegalAccessException * if the default constructor is not visible. * @throws InstantiationException * if the instance can not be created. */
?
看完之后豁然開朗啊有木有,原來這貨會去調用當前Class的無參構造方法去構造實例,并且還在后面非常牛逼的警告了,如果沒有無參構造方法或者構造失敗或者這個默認構造方法訪問不到,我就生氣就拋異常!再回過頭去看最開始貼出的異常信息,是不是和這里的異常message一模一樣呢,哈哈。
轉載于:https://www.cnblogs.com/duanzi6/p/6003693.html
總結
以上是生活随笔為你收集整理的Fragment为什么须要无参构造方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用admin lte 碰到访问Goog
- 下一篇: 有多少人的小拇指和我一样弯曲的?