insanelab.com insanelab.com

February 25, 2019 - Mobile Development

Advanced Xamarin Bindings Case Study – Deep Dive into Android – Part 1

1. Why We Should Use Software Libraries – Introduction to Reusable Building Blocks

Modern software development is all about code reuse. Developers often build essential, reusable code blocks while creating their best work. These blocks can be logged and stored in software libraries. Then, the well-organized libraries can later be referenced to solve real world problems.

To add map into our product we can use Google Maps library, to make Uber like experience for your user we can use HyperTrack (real-time user tracking experience).

If we need good looking animations – we can use Lottie library developed by AirBNB team and plug-in one of free animations which can be found here: https://www.lottiefiles.com/

Software Library Usage At Work (And Not At Work)

One of the reasons the Android and iOS environments have succeeded is because of the implementation of software libraries.  Both platforms are supported by a community of developers whom have compiled an enormous amount of free, reusable building blocks (a.k.a. programming libraries). This large reserve of coding data makes it easy to construct additional compatible applications.

There are numerous high-quality, license free libraries for iOS and Android available online. Most are written in Objective-C/Swift or Java/Kotlin.  If you need a fresh look for your mobile interface, you can explore a curated list of ready-to-go User Interface libraries for Android and iOS by clicking on the links below.

In contrast, the Windows Phone operating system was met with little success. Why? Because, it’s extremely difficult to build simple, engaging features (like Pull to Refresh) when only a limited number of Windows developers were involved in open source projects.  Windows’ lack of support and resources has discouraged outside developers from creating new products for Windows Phone. These types of projects were deemed too time-consuming and expensive to produce. In turn, the operating system’s limited versatility and minimal application selection has encouraged users to switch platforms!

2. How Cross-Platform Solutions Handle Native iOS/Android Libraries – And Why It’s Important

We have multiple cross-platform software development tools. These tools allow us to write important pieces of code once as opposed to several times. The developed code then has a transferable use across multiple platforms (for example Android and iOS).

In theory, all cross-platform development kits should increase efficiency and reduce project cost.  Unfortunately, most development tools make it incredibly difficult to plug in libraries that were built in default Android/iOS environments like Java, Kotlin, Objective-C or Swift, a level of compatibility that is an essential requirement in Modern Software Development.  For example, the most popular cross-platform development tools (like iONIC and React Native) will require you to write Objective C/Swift or Java code if you want to “bind(or reuse) native iOS/Android libraries!  This snag often forces companies to hire an additional Objective C/Java developer/contractor, as IONIC/React Native apps are built in HTML5 and/or JavaScript – which is a vastly different environment.

On the contrary, C#/.NET based Xamarin uses an automated generator approach (a.k.a. a Bindings Library project) to solve this problem. In theory, if you add an Android (.aar/.jar) or an iOS (.framework) library to the project, you will get an automatically generated wrapper to the Native Library.  In practice, you may need to take a few additional steps to make this work. Fortunately, these steps are usually straight-forward for a Senior Xamarin Developer.

3. Advanced Xamarin Bindings – Advanced Examples And How To Bind To The Native Android Library

We believe that there are many great articles related to basic Xamarin Bindings. So, we decided to cover lesser-known, more advanced examples and tips that will speed up the entire development process.

If you are unfamiliar with the concepts of basic Xamarin Android Library Binding, please review the following resource before you proceed further:  https://docs.microsoft.com/en-us/xamarin/android/platform/binding-java-library/

How Do You Quickly Get A Java Library And A List Of Required Dependencies?

 

Step One – Android Studio

Install Android Studio (you have probably done that before, haven’t you?) and create a new empty Android project.

Step Two – Add Gradle Dependency You Want To Bind

It’s close to guaranteed that the library you want to bind is available on Gradle (NuGet in Java world). Most libraries have their Gradle-name written in documentation/github.

In my case, it is:

com.hypertrack:android:0.6.25

 

Step Three – Build And Copy Your Library ?

When you added required dependency to Gradle just build project. During build the process, Gradle task will download all required dependencies.

Now you are done – just check the  ~/.gradle/caches/modules-2/files-2.1 folder on your Mac OSX.

All required dependencies should be present (even transitive ones). Make sure you check .pom file as well. You will have to add project dependencies to the bindings project as well.

4. Let The Fun Begin – Binding The Cumbersome Emoji Picker Library

One of the Android applications we have developed requires “Emoji” picker selection. The requirement is that each event we create is able to be represented by an “Emoji.”

First, we looked for some ready-to-use implementations to save development time. We found a brilliant java library by Niklas Baudy Vanniktech called Emoji. This is a well-developed, modular library. Install the main package “Emoji.” Then, install the Emoji Providers, so you can use multiple emoji types (Google-like/iOS/Twitter and so on).

Creating A Xamarin Android Bindings Project

This library did not have Xamarin Android bindings, so I had to generate them manually. I have added the output main library and the Google Compat Emoji Provider (.aar files) into the Android Bindings projects (binding generator).

Adding Android Library Dependencies To The Bindings Project

After I have added .aar files to the bindings project, I added the dependencies required by the Emoji library. (Note: This is usually a step where less advanced developers fail while creating Xamarin Bindings.)

If library A takes dependency to library B and C, the bindings project of library A have to reference the bindings or reference jars of libraries B and C.

To determine what we need, we have to look at the requested library build.gradle or .pom file (this is similar to Nuget packages.xml/project.json we use in .NET).
Here, I look at build.gradle of Emoji and Emoji Google Compat Provider:

As you can see, the Emoji library needs a reference to the Android Support AppCompat and CardView libraries. The Emoji-Google-Compat (Google Emojis Provider) requires reference to the original Emoji library and the Android Support Emoji Library.

These are reasonably common libraries in the Android world. Thus, I can just add the Nuget packages of those libraries to the Emoji and Emoji Google Compat Bindings Project.

 

5. First Error On Board – Let’s Fix Our Binding Library Build!

We start building our projects and we get…

GoogleCompatEmoji: cannot derive from sealed type “Emoji”.

What now? We need to check what the generated Com.Vanniktech.Emoji.Emoji.Emoji class looks like.

Let’s Look At The Automatically Generated Code

To see what the generated code looks like, toggle “Display all files” in Visual Studio for Mac option at solution level.

Then, expand “obj”/”generated/src” folders and open “Emojiclass.

As you can see, the class has a sealed modifier which means we can not inherit from that class. These kinds of problems arise because of C#/Java languages differences.  Not all of them are correctly resolved by the Xamarin Bindings generator. So, we must fix that manually.

Each bindings project has Transforms folder with “metadata.xml” file – which can be used to modify the generator output code.

We have a path to the generated class. We just need to remove the “sealed” attribute.
This can be done by using a final metadata attribute.

To read more about available transforms check: https://developer.xamarin.com/guides/android/advanced_topics/binding-a-java-library/customizing-bindings/java-bindings-metadata/ 

Using A Bindings Library In An Android Application

Both projects now build without any error, and everything seems to work. Now, it’s time to use the documentation of the original library and plug it into my app.

Unfortunately, the bindings generator did not generate the GoogleCompatEmojiProvider class, which is required for library setup. The other classes suggested by the documentation like EmojiPopup are available though.

6. An Impossible Error? Analyze Generator Output Once Again!

First, let’s check what classes are generated in the Emoji and Emoji Google Compat bindings project.

You will notice that we do not have a GoogleCompatEmojiProvider class. Strange.

We need to find the reason why this class has not been generated in the Bindings Project.
The only reliable way to do that is by checking the original Java code. Fortunately, this is an open-source project so we can do that. Please click on the link below to check the Java code.

https://github.com/vanniktech/Emoji/blob/master/emoji-google-compat/src/main/java/com/vanniktech/emoji/googlecompat/GoogleCompatEmojiProvider.java.

If the class is not generated, the problem usually lies in the fact that it uses a type that is not available in either in the bindings project or the referenced project.

Let’s look at the missing class definition!

public final class GoogleCompatEmojiProvider implements EmojiProvider, EmojiReplacer

This class implements the *EmojiReplacer* interface, but it is not available in the *Emoji library bindings* (see screens above).

What If We Don’t Have Access To The Source Code?

Is there any chance to finish bindings if they do not build, and we do not have access to the source code?

Sure thing!

All we have to do is decompile the original Java library (.aar/.jar). This should give us a source code good enough to complete our bindings.  There are many free tools to do this.  Personally, I use jd gui.

7. We Know The Reason For Failure – Is It Still Impossible To Fix?

So, we now know that in order to get GoogleCompatEmojiProvider that we need to get the EmojiReplacer interface in Bindings.

Let’s look at the original code once again. This time at let’s look at the *EmojiReplacer* interface.

https://github.com/vanniktech/Emoji/blob/ebe1cb05c38999dc4233655b8d754e33cacd4c71/emoji/src/main/java/com/vanniktech/emoji/EmojiReplacer.java

Honestly, I don’t know if it is possible to make the Bindings Generator automatically generate this interface for us. I suspect that there is a bug present which prevents it from generating classes that have strong cycles. The replaceWithImages method takes the EmojiReplacer method as a parameter.

 

8. So, Can We Use This Library in Xamarin Android?

Yes, we can. ?

However, the only way I have found to restore the EmojiReplacer class back is by writing it manually. ?

I have not found much documentation in regards to manually putting Java classes into Xamarin. Thus, I have analyzed and reverse-engineered the Bindings Generator output code.

To use Java types in C#, Xamarin uses a bunch of [Register] attributes and pointers (IntPtr).
In fact, it is not really intended to be used in “manual code.”

The mechanism is briefly described here: https://developer.xamarin.com/guides/android/advanced_topics/binding-a-java-library/customizing-bindings/java-bindings-metadata/

9. Register Attribute

Let’s start with analyzing the [Register] attribute.
To use Java code in C#, we have to use this attribute. In short, it tells the compiler how to control the C#-Java proxy.

The first parameter is name. This is the path to the type. In case the of the interface, the EmojiReplacer would be com/vanniktech/emoji/EmojiReplacer.

The second parameter is signature. This is used to determine the method signature (i.e. what kind of parameter it takes and what the return type is). For the interface, we should leave it empty -> “”.

The third parameter is connector. This is a managed class name which connects the native code with the managed code. This is why the bindings output contains a bunch of Invoker classes. They are called on whenever the original interface method is called.

10. EmojiReplacer Java Interface Bound To C#

  1. We have to add the IEmojiReplacer interface into the Bindings project.
  2. We have to register this interface. The EmojiReplacer sits in the com.vanniktech.emoji package so the “name” parameter is equal to “com/vanniktech/emoji/EmojiReplacer“.
  3. The EmojiReplacer is an interface, so we leave the signature parameter empty.
  4. We have to implement the connector class.  The managed code will be called on when the interface is used.  We will create a class for the IEmojiReplacerInvoker in the namespace Com.Vanniktech.Emoji. Then, we will put “Com.Vanniktech.Emoji.IEmojiReplacerInvoker” in the “connector” parameter.
  5. Looking at the EmojiReplacer, we can observe that it has just one method (replaceWithImages) and it takes five parameters.  We have to add the appropriate [Register] attribute. The first parameter, name, should be equal to the Java method name “, replaceWithImages.“ The second parameter – should describe the method signature.  Primitive types are described with one letter (i.e. “F” stands for float, “V” for void, etc.). Complex types like objects are prefixed with a namespace.  There is a “;” character after each complex type.  The parameter format is: (parameters description)ReturnType
  6. We have five parameters:
    a) Android.Content.Context – object so – “Landroid/content/Context;“.
    b) Android.Text.Spannable – object so – “Landroid/text/Spannable;“.
    c) Float – primitive type float so – “F“.
    d) Float – primitive type float so – “F“.
    e) IEmojiReplacer  – object which we have just registered to – “L/com/vanniktech/emoji/EmojiReplacer”
    The return type is void – so “V“.
    Putting that together we get:
    (Landroid/content/Context;Landroid/text/Spannable;FFL/com/vanniktech/emoji/EmojiReplacer;)V
  7. The third parameter should contain a path to the managed method of the previously described connector class.  The format for this parameter is:
    “registeredMethodName:namespace.ConnectorClassName, AssemblyWhereConnectorClassLies”.
11. EmojiReplacer Connector Class

The final connector class is presented below. It has been partially written manually. Thus, I will explain it in a moment.

[global::Android.Runtime.Register (“com/vanniktech/emoji/EmojiReplacer”, DoNotGenerateAcw=true)]
    internal class IEmojiReplacerInvoker : global::Java.Lang.Object, IEmojiReplacer {

        static IntPtr java_class_ref = JNIEnv.FindClass (“com/vanniktech/emoji/EmojiReplacer”);

        protected override IntPtr ThresholdClass {
            get { return class_ref; }
        }

        protected override global::System.Type ThresholdType {
            get { return typeof (IEmojiReplacerInvoker); }
        }

        IntPtr class_ref;
 

        static IntPtr Validate (IntPtr handle)
        {
            if (!JNIEnv.IsInstanceOf (handle, java_class_ref))
                throw new InvalidCastException (string.Format (“Unable to convert instance of type ‘{0}’ to type ‘{1}’.”,
                            JNIEnv.GetClassNameFromInstance (handle), “com.vanniktech.emoji.EmojiReplacer”));
            return handle;
        }

        protected override void Dispose (bool disposing)
        {
            if (this.class_ref != IntPtr.Zero)
                JNIEnv.DeleteGlobalRef (this.class_ref);
            this.class_ref = IntPtr.Zero;
            base.Dispose (disposing);
        }

        public IEmojiReplacerInvoker(IntPtr handle, JniHandleOwnership transfer) : base (Validate (handle), transfer)
        {
            IntPtr local_ref = JNIEnv.GetObjectClass (((global::Java.Lang.Object) this).Handle);
            this.class_ref = JNIEnv.NewGlobalRef (local_ref);
            JNIEnv.DeleteLocalRef (local_ref);
        }
        
        static IntPtr _replaceWithImagesMethodPointer;
        
        
        [Register (“replaceWithImages”, “(Landroid/content/Context;Landroid/text/Spannable;FFLcom/vanniktech/emoji/EmojiReplacer;)V”, “”)]
        public virtual unsafe void ReplaceWithImages(Context context, ISpannable text, float emojiSize, float defaultEmojiSize,
            IEmojiReplacer fallback)
        {
            if (_replaceWithImagesMethodPointer == IntPtr.Zero)
                _replaceWithImagesMethodPointer = JNIEnv.GetStaticMethodID (class_ref, “replaceWithImages”, “(Lcom/vanniktech/emoji/EmojiReplacer;)V”);
            try
            {
                JValue* __args = stackalloc JValue[5];
                __args[0] = new JValue(context);
                __args[1] = new JValue(text);
                __args[2] = new JValue(emojiSize);
                __args[3] = new JValue(defaultEmojiSize);
                __args[4] = new JValue(fallback);
                JNIEnv.CallVoidMethod((this).Handle, _replaceWithImagesMethodPointer, __args);
            } finally 
            {
            }        
        }
    }

Let’s start with the class definition. It has to inherit from Java.Lang.Object and implement the interface (for which it has been defined as a connector).

After sifting through the binding generator code, it turns out that we have to override two members, ThresholdClass and ThresholdType.

The ThresholdClass should point to the Java.Lang.Class for which it was registered as connector.  Use JNIEnv.FindClass(“path”) for that.
TheresholdType should return Invorker C# Type.

The Validate, Dispose, and Constructor have been copied from the other autogenerated binding class. This is responsible for the reference counting of the object.  You can just copy-paste that in your Invoker class.

The implementation of the IEmojiReplacer is what we really need here.
It points to the Java Method, creates method parameters, calls stack and, finally, calls the Java method using JNIEnv.CallVoidMethod(..).

We then allocate JValue arrays on stack using C# stackalloc (Thus, it won’t create garbage collector pressure. As far as I know, that was one of the optimizations made to the Bindings Generator by the Xamarin team, which had some positive performance impact on the Xamarin.Java code).

Make sure you set JValue array members within the original Java method parameters order, so the method is called properly with proper parameters.

12. Build And Have Fun!

After the rebuild, it works! The missing classes suddenly pop out.

The EmojiPicker library works as expected!

Hopefully, the advanced techniques and tricks I have demonstrated will help you deal with Xamarin Android Bindings issues in the near future!

If you are interested in reading the second part of this article, which will cover Advanced Xamarin iOS Bindings, and include tips and tricks that have never been published anywhere else leave a comment, share and give us feedback!

What is your challenge?

Tell us with any means provided. We'd love to hear from you!