3
votes

I am trying to implement Realm on a smaller Xamarin/Mvvmcross/iOS/Droid project in order to test it's ability to replace SQLite.

I have it working well on the iOS project but am getting exceptions on the Droid project when attempting to call Realm.GetInstance();

The Type initializer for 'Realms.Realm' threw an exception

Inner Exception System.Reflection.ReflectionTypeLoadException The classes in the module cannot be loaded.

I have narrowed it what I believe is an issue with reflection if the MvvmCross setup occurs before the Realm dll is loaded.

For example if I call Realm.GetInstance() in any activity that inherits from MvxActivity or MvxAppCompatActivity (or anywhere in the Mvvmcross Setup / CreateApp process) the exception occurs.

If however I call var db = Realm.GetInstance() (& db.Close()) from a normal Droid Activity first, and then start the Mvx Setup process, by starting an MvxActivity, from the Droid Activity it works fine, and continues to work through the application lifecycle.

Likewise if I subclass Application and open a Realm instance in OnCreate() and close it Real will initialise anywhere else in the application.

sample code

//works

[Application]
public class CustomApplication : Application
{
    public CustomApplication (IntPtr javaReference, JniHandleOwnership transfer) : base (javaReference, transfer)
    {
    }

    public override void OnCreate ()
    {
        base.OnCreate ();
        var db = Realm.GetInstance ();
        db.Close ();
    }
}

//does not work unless Realm.GetInstance() has already been called once

[Activity(Label = "View for FirstViewModel")]
public class FirstView : MvxActivity
{
    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);
        SetContentView(Resource.Layout.FirstView);
        var db = Realm.GetInstance ();
        db.Close ();
    }
}

I've put a test project on github at https://github.com/vurtigo/TestRealm

1
I tried running the project in the Xamarin Android Player (on an API 22 image) and it worked for me. What do you run the app on? - Tayschrenn
I've reset the test project to a non working version. I have tested on API 22 on the Xamarin Android Player. Turns out if you call Realm.GetInstance from MvxApplication.Initialize it will also work - and that code had been left in. It's now commented out. Thanks - Dean Marcussen
Thanks, I confirmed it. - Tayschrenn

1 Answers

0
votes

This is an unfortunate mix-up between Realm and Xamarin Android.

The static constructor on the Realm class walks through all the assemblies in the current AppDomain to discover all the types that inherit from RealmObject. However, if at the time Xamarin Android builds Java binding code this will define a new System.Reflection.Emit.AssemblyBuilder assembly that will raise a TypeLoadException when its types are enumerated (see Bug 39679 - ReflectionTypeLoadException after some reflection stuff).

The workaround is to cause the Realm static constructor to be invoked before any of the MvvmCross code causes Xamarin Android to emit binding code. You can do that by accessing any of the static members on Realm such as ReferenceEquals or even by including it in a typeof(Realm) expression. I suppose MvxApplication.Initialize() is a good place to do it.

In any case, I have proposed a fix that will ignore AssemblyBuilder instances in general. The very next Realm release should include it and you'll be able to delete the workaround code as soon as you upgrade.