編輯:關於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, ArrayList 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);
}
}
...
}
}
3. FragmentState的instantiate(Activity activity, Fragment parent)方法
最終會調用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能刷安卓嗎 魅藍metal刷基於安卓Android的flyme OS教程
魅族這次推出的魅藍metal是基於yunos版本的flymeOS,一些喜歡搞機的用戶當然希望能用回基於Android的flyme,那麼小編來給出一些消息和刷
Android性能優化系列之apk瘦身
為什麼APK要瘦身。APK越大,在下載安裝過程中,他們耗費的流量會越多,安裝等待時間也會越長;對於產品本身,意味著下載轉化率會越低(因為競品中,用戶有更多機會選擇那個體驗
android平台俄羅斯方塊游戲完整代碼
android studio 中小米系列手機布局問題
通過兩張圖對比,,不難發現布局異常!看代碼 android:layout_hei