Bug 28789 - Generic fragments throw typeloadexception
Summary: Generic fragments throw typeloadexception
Status: RESOLVED ANSWERED
Alias: None
Product: Android
Classification: Xamarin
Component: General ()
Version: 4.20.0
Hardware: PC Windows
: --- normal
Target Milestone: ---
Assignee: Jonathan Pryor
URL:
Depends on:
Blocks:
 
Reported: 2015-04-05 13:45 UTC by Miha Markic
Modified: 2015-04-23 13:12 UTC (History)
1 user (show)

Tags:
Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED ANSWERED

Description Miha Markic 2015-04-05 13:45:58 UTC
Consider the code below that creates a generic fragment. It works at first, but then, if activity gets destroyed (settings/developer options/don't keep activites) and recreated it'd throw System.TypeLoadException at base.OnCreate.
Perhaps fragments can't be generic?

    [Activity(Label = "App63", MainLauncher = true, Icon = "@drawable/icon")]
    public class MainActivity : Activity
    {
        int count = 1;

        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            // Set our view from the "main" layout resource
            SetContentView(Resource.Layout.Main);


            FragmentManager
                    .BeginTransaction()
                    .Add(new RhFrag<object>(), "xxx")
                    .Commit();
        }
    }

    public class RhFrag<T>: Fragment
    {}

Exception
System.TypeLoadException: A type load exception has occurred.


Call stack
 	0x24 in System.Delegate.CreateDelegate_internal	C#
 	0x35B in System.Delegate.CreateDelegate	C#
 	0x5 in System.Delegate.CreateDelegate	C#
 	0x3 in System.Delegate.CreateDelegate	C#
 	0x32 in System.Reflection.Emit.DynamicMethod.CreateDelegate	C#
 	0x122 in Android.Runtime.ConstructorBuilder.CreateDelegate at /Users/builder/data/lanes/monodroid-mlion-monodroid-4.21-series/38ac51a9/source/monodroid/src/Mono.Android/src/Runtime/ConstructorBuilder.cs:40,4	C#
 	0xBB in Java.Interop.TypeManager.n_Activate at /Users/builder/data/lanes/monodroid-mlion-monodroid-4.21-series/38ac51a9/source/monodroid/src/Mono.Android/src/Java.Interop/TypeManager.cs:155,4	C#
 	0x29 in object.d65a737d-e3e1-4b3a-8854-e6733426b398	C#
 	0x73 in Android.Runtime.JNIEnv.CallNonvirtualVoidMethod at /Users/builder/data/lanes/monodroid-mlion-monodroid-4.21-series/38ac51a9/source/monodroid/src/Mono.Android/src/Runtime/JNIEnv.g.cs:892,4	C#
 	0xA9 in Android.App.Activity.OnCreate at /Users/builder/data/lanes/monodroid-mlion-monodroid-4.21-series/38ac51a9/source/monodroid/src/Mono.Android/platforms/android-21/src/generated/Android.App.Activity.cs:2395,5	C#
>	0x10 in App63.MainActivity.OnCreate at g:\Temp\TempProjects\App63\App63\MainActivity.cs:18,-1	C#
 	0x13 in Android.App.Activity.n_OnCreate_Landroid_os_Bundle_ at /Users/builder/data/lanes/monodroid-mlion-monodroid-4.21-series/38ac51a9/source/monodroid/src/Mono.Android/platforms/android-21/src/generated/Android.App.Activity.cs:2380,4	C#
 	0x17 in object.9025c61c-2e9d-4aa0-9a59-62c7053a776f	C#
Comment 1 Jonathan Pryor 2015-04-13 15:35:21 UTC
> Perhaps fragments can't be generic?

Sorta, but not exactly.

You can have a generic Java.Lang.Object subclass, and pass it to Java code, and Java code can invoke methods on those instances. So long as managed code is creating the instance, it all works.

It falls apart when Java is creating the instance.. Java code cannot instantiate instances of generic types, because the Java code isn't -- and can't! -- provide generic type parameter information.

So the problem is that Java is trying to create an instance of the RhFrag_1 ACW type.

Where? More debugging later, and I get this Java callstack:

> at mono.android.TypeManager.n_activate(Native Method)
> at mono.android.TypeManager.Activate(TypeManager.java:7)
> at md5425d995fd978cc9110bfe59c4dde3a89.RhFrag_1.<init>(RhFrag_1.java:21)
> at java.lang.reflect.Constructor.newInstance(Native Method)
> at java.lang.Class.newInstance(Class.java:1606)
> at android.app.Fragment.instantiate(Fragment.java:611)
> at android.app.FragmentState.instantiate(Fragment.java:104)
> at android.app.FragmentManagerImpl.restoreAllState(FragmentManager.java:1775)
> at android.app.Activity.onCreate(Activity.java:933)
> at md5425d995fd978cc9110bfe59c4dde3a89.MainActivity.n_onCreate(Native Method)
> at md5425d995fd978cc9110bfe59c4dde3a89.MainActivity.onCreate(MainActivity.java:28)
> at android.app.Activity.performCreate(Activity.java:5990)
> at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106)
> at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2278)
> at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)
> at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3947)
> at android.app.ActivityThread.access$900(ActivityThread.java:151)
> at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1309)
> at android.os.Handler.dispatchMessage(Handler.java:102)
> at android.os.Looper.loop(Looper.java:135)
> at android.app.ActivityThread.main(ActivityThread.java:5254)
> at java.lang.reflect.Method.invoke(Native Method)
> at java.lang.reflect.Method.invoke(Method.java:372)
> at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
> at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

Rephrased: Activity.onCreate() calls FragmentManagerImpl.restoreAllState() which eventually calls Class.newInstance() which invokes the RhFrag<T> constructor...

...or *would* invoke it, if it could, which it can't, because the type 'T' is unknowable.

There is a workaround: Tell the Fragments API to *not* create new instances of this type, by setting Fragment.RetainInstance to true:

	var fragment = new RhFrag<object> ();
	fragment.RetainInstance = true;
	FragmentManager
		.BeginTransaction()
		.Add(fragment, "xxx")
		.Commit();

This prevents the Fragments API from creating new instances of your Fragment class, thus avoiding the issue.