Android. app. Fragment $ InstantiationException Cause Analysis

Source: Internet
Author: User

Android. app. Fragment $ InstantiationException Cause Analysis

1. Fragment $ InstantiationException Cause Analysis

When writing Fragment class code, Android Lint sometimes prompts the following error:


Avoid not-default constructors in fragments: use a default constructor plus Fragment $ setArguments (Bundle) instead

From the Fragment documentation:
Every fragment must have an empty constructor, so it can be instantiated when restoring its activity's state. it is stronugly recommended that subclasses do not have other constructors with parameters, since these constructors will not be called when the fragment is re-instantiated; instead, arguments can be supplied by the caller with setArguments (Bundle) and later retrieved by the Fragment with getArguments ().

Each Fragment must have a construction method without parameters, so that the Fragment can be instantiated only when the Activity recovers. It is strongly recommended that the child classes of Fragment do not have other constructor methods with parameters, because these constructor methods will not be called when Fragment is re-instantiated. Instead, set the parameters through setArguments (Bundle) and get the parameters through getArguments.

 

If the Fragment does not have a construction method without parameters, crash occurs when the app resumes the Activity (such as rotating the device.

 

Q: Why is there a no-argument constructor required, and the constructor containing parameters is not called when Fragment is re-instantiated?

Next, we will analyze the process of Activity restoration.

1. The onCreate (Bundle savedInstanceState) method of the Activity

 

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();        ...    }    ...}
When the savedInstanceState is not null, The restoreAllState (Parcelable state, ArrayList NonConfig) method to restore Fragment.

 

2. FragmentManagerImpl restoreAllState (Parcelable state, ArrayList NonConfig) Method

This method calls the instantiate (Activity activity, Fragment parent) method of the FragmentState.

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. instantiate (Activity activity, Fragment parent) method of FragmentState

 

The static factory method instantiate (Context context, String fname, @ Nullable Bundle args) of Fragment will be called ).

 

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 static factory method instantiate (Context context, String fname, @ Nullable Bundle args) 

 

The re-instantiation of Fragment uses the Java reflection mechanism and calls the non-argument constructor of Fragment. Therefore, this step will not call other constructor methods with parameters. Clazz. newInstance () throws InstantiationExecption, and then prints a common 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: In practice, there is another situation where the Fragment has no constructor and an InstantiationException is still thrown. Why?
When your Fragment is used as a non-static internal class, this Fragment cannot be instantiated using the Java reflection mechanism. In my actual project, a DialogFragment is defined as an internal class of Activity, resulting in this Exception. Because the instantiation of non-static internal class objects requires the first instantiation of external class objects, only the instantiation of non-static internal classes will inevitably throw the InstantiationException.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.