Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use MvvmCross plugins in Android without compiling DLL?

I have a problem using my own MvvmCross plugins in an Android project, because the PluginLoader seems to expect a .Droid.dll of the exact namespace it is in.

Say I have three projects with this folder and namespace structure:

MyApp.Core:
    Plugins/Settings/ISettings.cs
    Plugins/Settings/PluginLoader.cs
MyApp.Droid:
    Plugins/Settings/Settings.cs
    Plugins/Settings/Plugin.cs
    Bootstrap/SettingsPluginBootstrap.cs
MyApp.Touch:
    Plugins/Settings/Settings.cs
    Plugins/Settings/Plugin.cs
    Bootstrap/SettingsPluginBootstrap.cs

The iOS project works just as expected, and finds the plugin without problem.

The Droid project on the other hand, fails with an exception: Could not load file or assembly 'MyApp.Plugins.Settings.Droid.dll' or one of its dependencies.

If I change the namespace of the PluginLoader from MyApp.Core.Plugins.Settings to simply MyApp, the plugin works; I guess it looks for the MyApp.dll and finds it. However, if I have multiple plugins in my app, they should each have their own namespace, they can't all be in the MyApp namespace.

Currently the only workaround I have found is to create a separate project for each plugin I create, though this feels a bit unnecessary.

Why does the PluginLoader insist on looking for the <PluginLoader namespace>.Droid.dll file on Android, when the PluginLoader on iOS finds the plugin without any problem?

like image 894
Geir Sagberg Avatar asked Oct 03 '22 10:10

Geir Sagberg


1 Answers

MvvmCross plugins are designed to work as an assembly based layer on top of IoC.

By using a shared pattern for namespaces and assemblies, along with a couple of helper classes (Plugin and PluginLoader), plugins provide a way to share and reuse portable native code components.

The reason why iOS (and Mac) use a slightly different plugin loading scheme is because the MonoTouch's AoT compiler will not allow dynamic Assembly.Load loading.

Because of this iOS has to use a different type of PluginManager and a different type of Bootstrap class to the other platforms. There's a bit more info on this in "How plugins are loaded" in https://github.com/MvvmCross/MvvmCross/wiki/MvvmCross-plugins#how-plugins-are-loaded


If you wanted to add the Loader type plugin registry to Android as well as iOS, then I think you could do this in a custom PluginManager class and could then create this during Setup using an override of protected override IMvxPluginManager CreatePluginManager().

Something like:

public class MyPluginManager : MvxFilePluginManager, IMvxLoaderPluginManager
{
    private readonly Dictionary<string, Func<IMvxPlugin>> _finders = new Dictionary<string, Func<IMvxPlugin>>();

    public MyPluginManager(string platformDllPostfix, string assemblyExtension = "") : base(platformDllPostfix, assemblyExtension)
    {
    }

    public IDictionary<string, Func<IMvxPlugin>> Finders
    {
        get { return _finders; }
    }

    protected override IMvxPlugin FindPlugin(Type toLoad)
    {
        var pluginName = toLoad.Namespace;
        if (string.IsNullOrEmpty(pluginName))
        {
            throw new MvxException("Invalid plugin type {0}", toLoad);
        }

        Func<IMvxPlugin> finder;
        if (_finders.TryGetValue(pluginName, out finder))
        {
            return finder();
        }

        return base.FindPlugin(toLoad);
    }
}

initialised in your Setup using:

    protected override IMvxPluginManager CreatePluginManager()
    {
        return new MyPluginManager(".Droid", ".dll");
    }

you would then need to ensure that your loader based plugins uses bootstrap classes based off of MvxLoaderPluginBootstrapAction and not MvxPluginBootstrapAction


As another alternative, if you are not interested in reusing your plugins individually - if you would rather not ship lots of individual plugin assemblies - then you could put all your interfaces and implementations into one single assembly - and they could then share a single Bootstrap, Plugin and PluginLoader between them.


As a final alternative, for your custom needs, you can always consider using your own custom bootstrap classes - the standard Setup will create and Run any constructable class in your UI assembly which implements IMvxBootstrapAction - so you could replace SettingsPluginBootstrap with some custom Run action that suits your apps needs.

public class SettingsBootstrapAction
    : IMvxBootstrapAction
{
    public void Run()
    {
        // my stuff here
    }
}
like image 186
Stuart Avatar answered Oct 07 '22 19:10

Stuart