Embeddinator-4000

Tools to turn .NET libraries into native libraries that can be consumed on Android, iOS, Mac, Linux and other platforms.

View the Project on GitHub

Getting started with Android

In addition to the requirements from our Getting started with Java guide you’ll also need:

As an overview, we will:

Create an Android Library Project

Open Visual Studio for Windows or Mac, create a new Android Class Library project, name it hello-from-csharp, and save it to ~/Projects/hello-from-csharp or %USERPROFILE%\Projects\hello-from-csharp.

Add a new Android Activity named HelloActivity.cs, followed by an Android Layout at Resource/layout/hello.axml.

Add a new TextView to your layout, and change the text to something enjoyable.

Your layout source should look something like this:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:minWidth="25px"
    android:minHeight="25px">
    <TextView
        android:text="Hello from C#!"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center" />
</LinearLayout>

In your activity, make sure you are calling SetContentView with your new layout:

[Activity(Label = "HelloActivity"), 
    Register("hello_from_csharp.HelloActivity")]
public class HelloActivity : Activity
{
    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);

        SetContentView(Resource.Layout.hello);
    }
}

NOTE: don’t forget the [Register] attribute, see details under Limitations

Build the project, the resulting assembly will be saved in bin/Debug/hello-from-csharp.dll.

Installing Embeddinator-4000 from NuGet

Choose Add > Add NuGet Packages… and install Embeddinator-4000 from the NuGet package manager: NuGet Package Manager

This will install Embeddinator-4000.exe into the packages/Embeddinator-4000/tools directory.

Run Embeddinator-4000

We will add a post-build step to run Embeddinator and create a native AAR file for the Android library project assembly.

In Visual Studio for Mac, go to Project Options > Build > Custom Commands and add an After Build step.

Setup the following commnd:

mono '${SolutionDir}/packages/Embeddinator-4000.0.2.0.80/tools/Embeddinator-4000.exe' '${TargetPath}' --gen=Java --platform=Android --outdir='${SolutionDir}/output' -c

NOTE: make sure to use the version number you installed from NuGet

If you are going to be doing ongoing development on the C# project, you might also add a custom command to clean the output directory prior to running Embeddinator:

rm -Rf '${SolutionDir}/output/'

Custom Build Action

The Android AAR file will be placed in ~/Projects/hello-from-csharp/output/hello_from_csharp.aar. NOTE: hyphens are replaced because Java does not support it in package names.

Running Embeddinator on Windows

We will essentially setup the same thing, but the menus in Visual Studio are a bit different on Windows. The shell commands are also slightly different.

Go to Project Options > Build Events and enter the following into the Post-build event command line box:

set E4K_OUTPUT="$(SolutionDir)output"
if exist %E4K_OUTPUT% rmdir /S /Q %E4K_OUTPUT%
"$(SolutionDir)packages\Embeddinator-4000.0.2.0.80\tools\Embeddinator-4000.exe" "$(TargetPath)" --gen=Java --platform=Android --outdir=%E4K_OUTPUT% -c

Such as the following screenshot:

Embeddinator on Windows

Use the generated output in an Android Studio project

Open Android Studio and create a new project with an Empty Activity.

Right-click on your app module and choose New > Module. Select Import .JAR/.AAR Package. Use the directory browser to locate ~/Projects/hello-from-csharp/output/hello_from_csharp.aar and hit Finish.

Import AAR into Android Studio

This will copy the AAR file into a new module named hello_from_csharp.

Android Studio Project

To use the new module from your app, right-click and choose Open Module Settings. On the Dependencies tab, add a new Module Dependency and choose :hello_from_csharp.

Android Studio Dependencies

In your activity, add a new onResume method, and let’s do the easiest thing to launch our C# activity:

import hello_from_csharp.*;

public class MainActivity extends AppCompatActivity {
    //... Other stuff here ...
    @Override
    protected void onResume() {
        super.onResume();

        Intent intent = new Intent(this, HelloActivity.class);
        startActivity(intent);
    }
}

Assembly Compression IMPORTANT

One further change is required for Embeddinator in your Android Studio project.

Open your app’s build.gradle file and add the following change:

android {
    // ...
    aaptOptions {
        noCompress 'dll'
    }
}

Xamarin.Android currently loads .NET assemblies directly from the APK, but it requires the assemblies to not be compressed.

If you do not have this setup, the app will crash on launch and print something like this to the console:

com.xamarin.hellocsharp A/monodroid: No assemblies found in '(null)' or '<unavailable>'. Assuming this is part of Fast Deployment. Exiting...

Run the app

Upon launching your app:

Hello from C# sample running in the emulator

Note what happened here:

So for this sample to work, all the following are setup in the final APK:

If you are looking for an additional walkthrough, check out this video embedding Charles Petzold’s FingerPaint demo in an Android Studio project here:

Embeddinator-4000 for Android

Using Java 1.8

As of writing this, the best option is to use Android Studio 3.0 beta (download here).

To enable Java 1.8 in your app module’s build.gradle file:

android {
    // ...
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

You can also take a look at our Android Studio test project here for more details.

If you are wanting to use Android Studio 2.3.x stable, you will have to enable the deprecated Jack toolchain:

android {
    // ..
    defaultConfig {
        // ...
        jackOptions.enabled true
    }
}

Current Limitations on Android

Right now if you subclass Java.Lang.Object, Xamarin.Android will generate the Java stub (Android Callable Wrapper) instead of Embeddinator.

So you must follow the same rules for exporting C# to Java as Xamarin.Android.

So for example in C#:

    [Register("mono.embeddinator.android.ViewSubclass")]
    public class ViewSubclass : TextView
    {
        public ViewSubclass(Context context) : base(context) { }

        [Export("apply")]
        public void Apply(string text)
        {
            Text = text;
        }
    }

We can use ViewSubclass in Java like so:

import mono.embeddinator.android.ViewSubclass;
//...
ViewSubclass v = new ViewSubclass(this);
v.apply("Hello");

Read more about Java integration with Xamarin.Android here.

Multiple Assemblies

Embedding a single assembly is straightforward; however, it is much more likely you will have more than one C# assembly. Many times you will have dependencies on NuGet packages such as the Android support libraries or Google Play Services that further complicate things.

This causes a dilemma, since Embeddinator needs to include many types of files into the final AAR such as:

You most likely do not want to include these files from the Android support library or Google Play Services into your AAR, but would rather use the official version from Google in Android Studio.

Here is the recommended approach:

So your command might be:

mono MonoEmbeddinator4000.exe --gen=Java --platform=Android -c -o output YourMainAssembly.dll YourDependencyA.dll YourDependencyB.dll

You should exclude anything from NuGet, unless you find out it contains Android assets, resources, etc. that you will need in your Android Studio project. You can also omit dependencies that you do not need to call from Java, and the linker should include the parts of your library that are needed.

To add any Java dependencies needed in Android Studio, your build.gradle file might look like:

dependencies {
    // ...
    compile 'com.android.support:appcompat-v7:25.3.1'
    compile 'com.google.android.gms:play-services-games:11.0.4'
    // ...
}

Further Reading