Custom controls in the Xamarin Android Designer

garuma

Jérémie Laval

Posted on March 31, 2019

Custom controls in the Xamarin Android Designer

aka an introductory look at the internals of our Android ecosystem

In the latest stable release of the Xamarin platform, one specific feature I worked on is custom controls support in the Android designer (both Mac with Xamarin Studio and Windows on Visual Studio).

Our iOS designer has long enjoyed this kind of support, actually from day 1. Being part of that team at the time, I can confirm we were quite excited (and relieved!) to see that pie chart renders on stage when Miguel introduced the feature at Evolve 2013:

iOS designer custom controls

However, because the rendering strategy is quite different between the two designers, attaining the same level of support for custom controls on Android was much more challenging.

In this post I want to highlight how the Android designer and, more generally, Xamarin.Android work and how implementing custom controls fitted into this.

Designer Architecture

The Android designer is roughly split into two main parts. The first one is written in C# and constitute the bulk of the code. It’s a cross-IDE library that implements all the visual pieces of a modern designer like a surface canvas or a property panel and all the plumbing to make those work including a full DOM for Android layouts.

The second part is implemented in Java and, unlike the previous part that is run together with Xamarin Studio or Visual Studio, the Java layer is run into its own dedicated process. To summarize what it does, let’s say that it mostly handles the heavy-lifting of parsing resources and rendering Android layouts.

That last duty specifically (rendering layouts) is actually accomplished by using an existing Google library called layoutlib. It’s the same underlying library that powers the designer in Android Studio. Its goal is to take an XML layout, inflate it, layout it and render it to an image buffer.

Layoutlib is pretty nifty, it works by reusing an existing android.jar (which only contains the raw unimplemented Android API surface) and dynamically wires up enough implementation for classes and methods to be able to run a part of the Android experience on desktop. Key APIs are re-implemented using standard JRE libraries, a good example being the Android Canvas API that is translated to equivalent AWT calls for instance.

This process is done dynamically behind the scenes when you instantiate the library. It will automatically fetch the android.jar archive corresponding to the API level you want to use and plug it with the desktop implementation that is contained in another archive in the platform (aptly named layoutlib.jar ). If you are curious, you can also view that code online.

The end result is a Java ClassLoader instance that allow you to retrieve and instantiate all of the Android framework classes like android.view.View. This ClassLoader isolation is actually very handy because it allows to load several version of the platform at the same time (i.e. different android.view.View definitions) without polluting the main process class space.

In addition to this, layoutlib uses a system of callback to let consumers of the library provide additional input. One of such callback is invoked when layoutlib tries to inflate an item in a layout that it doesn’t know about because it doesn’t come from the framework ClassLoader I described above. As you probably guessed, those items are the custom user views.

Tying in Custom Controls

Because Xamarin.Android lets you use both Java and .NET based components (the former being wrapped into bindings, including our Android support libraries components), it was essential to support both of them in the designer.

Java components are actually fairly easy since the rendering process is already running in a JVM. You simply have to reference the custom component .jar code (usually distributed as part of an Android library project in a .aar file) into something like a URLClassLoader instance and wire the loading in the layoutlib callback I mentioned.

The fun stuff is obviously to include the managed components. But before we dwelve into that too much, let’s make a quick recap on how Xamarin.Android works or, more precisely, how it integrates with the Java virtual machine it’s running on.

Putting some C# in your Java

What I am going to describe here mostly relates to our legacy implementation in Xamarin.Android although much of the same ideas still live on in our new Java.Interop library (essentially in a cleaned up way).

The goal of Xamarin.Android in fine is to allow managed code to be run inside an existing Java runtime and ensure both of them can talk to each other. To accomplish this, it embeds the Mono runtime inside the process and allows it to coexist with the existing Java VM.

To allow communication between the two worlds, it uses the concept of bridged objects. Everytime a Java object is surfaced to the managed world (e.g. a overriden method parameter) or when a managed (likely autogenerated) wrapper to a Java object is created, a peering process happens whereby the managed instance and the Java instance become linked to each other, sharing their lifetime (meaning one can’t be collected without the other) until the link is cut.

To call Java code from the managed world, the JNI invocation API is used. To let Java code call into the managed world (due to subclassing or callbacks), a Java wrapper class is automatically generated.

Those wrappers (called ACW/JCW for Android/Java Callable Wrappers) contain a bunch of native methods definition to act as thunks. The thunks are then dynamically plugged using the RegisterNatives JNI call with a function pointer into essentially a mono_runtime_invoke call.

For instance, take the following MyCustomView subclass written in C# like this with its OnDraw method overriden:

public class MyCustomView: Android.Views.View
{
    public override OnDraw (Canvas c)
    {}
}
Enter fullscreen mode Exit fullscreen mode

During build, an equivalent Java file will be generated, compiled and packaged in your APK:

package md55d31ab91effba0f9ed7ec79c59c38391;

public class MyCustomView
       extends android.view.View
       implements mono.android.IGCUserPeer
{
        public void onDraw (android.graphics.Canvas p0)
        {
                n_onDraw (p0);
        }

        private native void n_onDraw (android.graphics.Canvas p0);
}
Enter fullscreen mode Exit fullscreen mode

When you reference that custom view in your XML layout you will likely do it in this fashion:

<MyNamespace.MyCustomView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
Enter fullscreen mode Exit fullscreen mode

With some extra Xamarin.Android processing, this will tell the Android layout inflater to load the Java class previously generated and, when trying to draw the view, will end up calling the managed implementation of the OnDraw method through its native thunk.

Back to custom controls

Now that we have a clearer picture on how managed C# views are instantiated in an Android environment, it’s very easy to get them loaded into the designer. Simply add their generated ACW to the special URLClassLoader I mentioned beforehand and they will get processed like any other standard Java custom views.

Only thing left to do was to port our Xamarin.Android/Mono runtime to be able to run natively inside a standard Java desktop VM (in the same fashion than layoutlib with the Android API) so that it could execute the other side of that shim custom view. That code is also now open-source and you can view it on GitHub.

As part of this porting work some dusting off of our AppDomain implementation was required, especially to ensure it worked reliably in a “mobile” context. AppDomains are used as a natural complement to Java’s class loaders allowing the same silo’ed approach on both the Java and managed side.

Thanks to this association, the designer can create several context where multiple version of Android can be run together in an isolated fashion and refreshed binaries can be reloaded without the need for the renderer process itself to be torn down.

Finishing up

Android designer custom controls

The layout rendered above extracted from one of my demo project contains a lot of different type of custom controls like AppBarLayout, Toolbar, RecyclerView, CoordinatorLayout and custom views for FloatingActionButton. By the way, the same feature is what’s also used in our Xamarin.Forms previewer to display the Android rendering of a layout.

There are more things that could be covered here, specifically I never mentioned how we handle error cases or even critical runtime failures which are bound to happen when you load external user code anywhere. Suffice to say we need some good error handling and process resurrection mechanisms which were definitely some of the hardest aspects to implement in the project (and still are).

Not all the code for what’s here is open-source but there are a quite few more things to look at if you are interested:

💖 💪 🙅 🚩
garuma
Jérémie Laval

Posted on March 31, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related