androidxamarinpreferencefragmentinstantiationexception

InstantiationException: Unable to instantiate fragment make sure class name exists, is public, and has an empty constructor that is public


I have a project that I originally developed using Android Studio. I decided to convert it to Xamarin (Visual Studio 2015).

After hours of porting all the code over, everything works except for my Settings activity (PreferenceActivity). I have a few PreferenceFragments that make up the settings, but all of them give me "Unable to instantiate fragment". Here is the exception I am getting:

Java.Lang.RuntimeException: Unable to start activity ComponentInfo{test.mypackagename/md50d00e677e41fc49f8b3c16e79df2b77f.SettingsActivity}: android.app.Fragment$InstantiationException: Unable to instantiate fragment test.mypackagename.GeneralPreferenceFragment: make sure class name exists, is public, and has an empty constructor that is public

I have been looking online for a solution but I just cant seem to find one. Everywhere I look they say make sure there is an empty public constructor, if its an inner class it has to be static. But I have the empty constructor and its not an inner class, its in its own file.

Here is my SettingsActivity.cs:

namespace test.mypackagename
{
    public class SettingsActivity : PreferenceActivity
    {
        protected override void OnPostCreate(Bundle savedInstanceState)
        {
            base.OnPostCreate(savedInstanceState);
        }

        public override void OnBuildHeaders(IList<Header> target)
        {
            LoadHeadersFromResource(Resource.Xml.pref_headers, target);
        }
    }
}

Here is my GeneralPreferenceFragment.cs:

namespace test.mypackagename
{
    public class GeneralPreferenceFragment : PreferenceFragment
    {
        public GeneralPreferenceFragment() { }

        public override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);
            AddPreferencesFromResource(Resource.Xml.pref_general);
        }
    }
}

And here is my pref_headers.xml:

<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">

    <header android:fragment="test.mypackagename.GeneralPreferenceFragment"
        android:title="@string/pref_header_general" />

    <header android:fragment="test.mypackagename.OtherPreferenceFragment1"
        android:title="@string/pref_header_other1" />

    <header android:fragment="test.mypackagename.OtherPreferenceFragment2"
        android:title="@string/pref_header_other2" />

    <header android:fragment="test.mypackagename.OtherPreferenceFragment3"
        android:title="@string/pref_header_other3" />

    <header android:fragment="test.mypackagename.OtherPreferenceFragment4"
        android:title="@string/pref_header_other4" />

</preference-headers>

This was working fine before so Im not sure what the issue could be. Any help would be much appreciated.


Solution

  • I think you are running into this problem because when not using the [Register] attribute on your PreferenceFragment then its name appended with a MD5 sum by Xamarin.

    So in order to actually have the namespace you expect it to in the pref_headers.xml you need to attribute your class:

    [Register("test.mypackagename.GeneralPreferenceFragment")]
    public class GeneralPreferenceFragment: PreferenceFragment
    {
        // code here
    }
    

    EDIT:

    I've just tested the code and it works fine on my machine. I am not using any support packages or anything.

    pref_general.xml

    <?xml version="1.0" encoding="utf-8" ?> 
    <PreferenceScreen
            xmlns:android="http://schemas.android.com/apk/res/android">
        <PreferenceCategory
                android:title="durr">
            <CheckBoxPreference
                    android:key="checkbox_preference"
                    android:title="herp"
                    android:summary="derp" />
        </PreferenceCategory>
    </PreferenceScreen>
    

    pref_headers.xml

    <?xml version="1.0" encoding="utf-8" ?>
    <preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
      <header android:fragment="test.mypackagename.GeneralPreferenceFragment"
          android:title="general" />
    </preference-headers>
    

    SettingsActivity.cs

    [Activity(Label = "SettingsActivity")]
    public class SettingsActivity : PreferenceActivity
    {
        public override void OnBuildHeaders(IList<Header> target)
        {
            LoadHeadersFromResource(Resource.Xml.pref_headers, target);
        }
    }
    

    GeneralPreferenceFragment.cs

    [Register("test.mypackagename.GeneralPreferenceFragment")]
    public class GeneralPreferenceFragment : PreferenceFragment
    {
        public override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);
    
            AddPreferencesFromResource(Resource.Xml.pref_general);
        }
    }
    

    This works fine and the app shows up the SettingsActivity with first a general option, after clicking on that it shows up a Title and CheckBox.

    General CheckBoxPreference

    This worked even without providing any ctor to the GeneralPreferenceFragment. However, you could try add these:

    public GeneralPreferenceFragment()
    {
    }
    
    public GeneralPreferenceFragment(IntPtr javaRef, JniHandleOwnership transfer)
        : base(javaRef, transfer)
    {
    }
    

    The latter ctor is often needed when the app is coming back from background or when the Java world is invoking the class somehow.