編輯:關於Android編程
1. Fragment$InstantiationException的原因分析
在編寫Fragment類的代碼時候,Android Lint有時會提示如下error:
如果的Fragment沒有無參構造方法,app在恢復Activity時(例如旋轉設備),會出現crash。
Q: 為什麼必須要有一個無參構造方法,而且含參構造方法在Fragment重新實例化時不會調用?
接下來分析一下Activity恢復狀態的過程。
1. Activity的onCreate(Bundle savedInstanceState)的方法
package android.app; public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2, Window.OnWindowDismissedCallback { final FragmentManagerImpl mFragments = new FragmentManagerImpl(); protected void onCreate(@Nullable Bundle savedInstanceState) { ... if (savedInstanceState != null) { Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG); mFragments.restoreAllState(p, mLastNonConfigurationInstances != null ? mLastNonConfigurationInstances.fragments : null); } mFragments.dispatchCreate(); ... } ... }當savedInstanceState不為null的時候,會調用FragmentManagerImpl的restoreAllState(Parcelable state, ArrayList
2. FragmentManagerImpl的restoreAllState(Parcelable state, ArrayList
該方法會調用FragmentState的instantiate(Activity activity, Fragment parent)方法。
package android.app; final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 { ... void restoreAllState(Parcelable state, ArrayList3. FragmentState的instantiate(Activity activity, Fragment parent)方法nonConfig) { // If there is no saved state at all, then there can not be // any nonConfig fragments either, so that is that. if (state == null) return; FragmentManagerState fms = (FragmentManagerState)state; if (fms.mActive == null) return; ... for (int i=0; i (); } if (DEBUG) Log.v(TAG, "restoreAllState: avail #" + i); mAvailIndices.add(i); } } ... } }
最終會調用Fragment的靜態工廠方法instantiate(Context context, String fname, @Nullable Bundle args)。
package android.app; final class FragmentState implements Parcelable { ... public Fragment instantiate(Activity activity, Fragment parent) { if (mInstance != null) { return mInstance; } if (mArguments != null) { mArguments.setClassLoader(activity.getClassLoader()); } mInstance = Fragment.instantiate(activity, mClassName, mArguments); if (mSavedFragmentState != null) { mSavedFragmentState.setClassLoader(activity.getClassLoader()); mInstance.mSavedFragmentState = mSavedFragmentState; } mInstance.setIndex(mIndex, parent); mInstance.mFromLayout = mFromLayout; mInstance.mRestored = true; mInstance.mFragmentId = mFragmentId; mInstance.mContainerId = mContainerId; mInstance.mTag = mTag; mInstance.mRetainInstance = mRetainInstance; mInstance.mDetached = mDetached; mInstance.mFragmentManager = activity.mFragments; if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG, "Instantiated fragment " + mInstance); return mInstance; } }
4. Fragment的靜態工廠方法instantiate(Context context, String fname, @Nullable Bundle args)
Fragment的重新實例化是利用Java反射機制,並且調用的是Fragment的無參構造方法,所以這一步是不會調用其他有參構造方法的。若Fragment沒有無參構造方法,則clazz.newInstance()會拋出InstantiationExecption,然後就會打印出常見的一個Exception,"Unable to instantiate fragment " + fname + ": make sure class name exists, is public, and has an" + " empty constructor that is public"。
package android.app; public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListener { private static final ArrayMap> sClassMap = new ArrayMap >(); public static Fragment instantiate(Context context, String fname, @Nullable 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); } } }
Q: 實際中還會遇到另一種情況,該Fragment有無參構造方法,依然拋出了InstantiationException。這又是為什麼呢?
魅族這次推出的魅藍metal是基於yunos版本的flymeOS,一些喜歡搞機的用戶當然希望能用回基於Android的flyme,那麼小編來給出一些消息和刷
為什麼APK要瘦身。APK越大,在下載安裝過程中,他們耗費的流量會越多,安裝等待時間也會越長;對於產品本身,意味著下載轉化率會越低(因為競品中,用戶有更多機會選擇那個體驗
通過兩張圖對比,,不難發現布局異常!看代碼 android:layout_hei