Embedding C# in Native Android

Photo by Rustic Vegan on Unsplash

Microsoft recently announced .NET Embedding for consuming C# libraries in an Android app.

If preview technology seems questionable, you can also embed C# using Xamarin.Android.

This is also questionable, but not in preview.

Here is the basic dependency structure. The arrows show a “depends on” relationship:

Dependencies

The App module uses an implementation of the Kotlin IHelloService provided by ServiceFactory. ServiceFactory supplies an Android Callable Wrapper of the C# HelloXamarinService class.

Setup

On the Android Studio side, create an application with three modules:

  1. App
  2. Xamarin
  3. XamarinInterface

The Xamarin module builds and integrates C# dependencies.

The XamarinInterface module provides separation between signature and implementation.

On the Xamarin side, create an application with three projects:

  1. XamarinDependency.Application
  2. XamarinDependency.Library
  3. XamarinDependency.BindingsLibrary

Pro Tip: Resist the urge to suffix your application project with .App. MacOS will treat the folder as an application bundle in Finder.

Gradle XamarinInterface module

Create a Kotlin IHelloService interface that you will later implement in C#.

Keep this module as low-level as possible with minimal dependencies.

Android dependencies will need corresponding Nuget packages in Xamarin. Particularly if those dependencies contain resources.

Xamarin BindingsLibrary project

This C# project accepts a jar file that contains the JVM bytecode of the interface module classes.

It generates Managed Callable Wrappers for the Kotlin interfaces and abstract classes.

This project only needs to exist as a dependency.

Xamarin Library project

This C# project provides implementations of the Kotlin interfaces.

Add a project dependency on the bindings library, and create the HelloXamarinService here.

To generate an Android Callable Wrapper, HelloXamarinService must derive from Java.Lang.Object. An abstract class would also work here.

Use RegisterAttribute to define the full class name of the Android Callable Wrapper.

ExportAttribute also generates an Android Callable Wrapper for a method. But implementing a Managed Callable Wrapper will allow combined C#/Kotlin stack traces.

Xamarin Application project

This C# project will generate an APK which we will unpack and merge into our Android Studio APK.

In the project settings, add a dependency on the Xamarin library project. Match up the TargetFrameworkVersion with your Gradle compileSdkVersion.

A compileSdkVersion of 26 would correspond to a TargetFrameworkVersion of v8.0.

Disable using the latest platform SDK. Disable using the shared Mono runtime. Enable x86 support if you want to use an emulator.

In the .csproj:

Gradle Xamarin module

This is where you configure the MSBuild C# build. Here the full sample build.gradle.

Add dependencies on the XamarinInterface module and on mono.android.jar:

The api dependency configuration exports the dependency to the App module.

Add tasks for copying an interface jar into the Xamarin bindings library:

Add tasks for building the C# project:

Add tasks for unpacking the APK artifact:

We need these files from the Xamarin APK:

  1. assemblies/** Compiled C# .dll files.
  2. lib/** Compiled C++ .so files, including any ahead-of-time compiled C# and mono libraries.
  3. typemap.** Type mappings used by Xamarin.
  4. environment Mono environment settings.
  5. NOTICE Says that this contains open source code. Why not.

Add the unpacked directory to the Java source set to configure APK packaging.

C# assemblies are memory mapped, so store them uncompressed.

Exclude dll extensions from compression in the app build.gradle.

Add tasks for cleaning the Xamarin projects:

Wire up the task dependencies:

An Android content provider initializes the Mono runtime.

Build the app, then copy the provider from the Xamarin manifest to the manifest in the Xamarin module.

The manifest is in the XamarinDependency.Application/obj/Debug/android directory.

Run it!

At this point you can build the entire project in Android Studio.

Xamarin code is only built when files have changed. Android Studio instant run works as expected.

The Xamarin build is stock, insulating us from breaking updates.

The XamarinInterface module is at the lowest level. Dependencies on the Kotlin side will not need corresponding Nuget packages in Xamarin.

Debugging C# with Visual Studio for Mac requires a custom plugin, which I plan to cover in a future article.

Considerations

In testing, you will want to look at many factors before committing to this approach.

Measure build time for changes in Kotlin, C#, and a full build in both debug and release.

Measure the increase in your APK size. Drag your APK onto Android Studio to view file sizes. Proguard and C# linking may help reduce file sizes.

Measure the difference in startup time in both debug and release. Depending on the structure of your application, you may be able to reduce the impact on startup time. Ahead-of-time compilation may increase performance at the expense of APK size.

Profile resource usage in both debug and release.

Test C# exception handling in both debug and release to ensure adequate stack traces. Ensure that your Xamarin project configuration has the appropriate level of debug information.

Weight the cost of adding this level of complexity to your project.

Full source code for KotlinAppWithXamarinDependency and XamarinDependency is on GitHub.

Hope this helps, good luck!

Android Software Engineer