Troubleshooting for Xamarin.Android binding project

Background

One of the most common issues that we will meet when developing with Xamarin is that there are lots of the libraries exist in Android or iOS but not in .NET. It’s the same reason as WinPhone’s failure. There are lots of applications exist in Android and iOS but you cannot find in Microsoft Store.

To resolve this issue, Xamarin has a project type which is called Binding project. Using this, you will be able to binding a jar/aar package under Android or objective-C library under iOS in Xamarin project as a dll library. You can even import a native library written in C/C++ (but we will not cover in this article).

In this article, we will focus on how to bind a jar package in Xamarin. Aar package is the same actually only with some resouces packaged in that library.

Content

There are 2 aspects in this article. First is that I will reference an article written by MS senior developer for Xamarin, these are the concepts on how to do the troubleshooting. The second aspect is what I have done while dealing with Xamarin.Android binding project and how I used the knowledge in the first aspect.


Skills and concepts for troubleshooting Xamarin.Android binding project

1. Research (Preparation)

When we start to troubleshoot the binding library issue, we need to install some useful tools as beow:

After the tools are ready, we will do the following steps to analyze the issue.

  1. The binding project with the build issue.
  2. Full Diagnostic Build log from build result
  3. Analyze the errors according to the Diagnostic Build log

At this time, we do not need to check on the detail error messages. What we need to check is the basic information for the issue library. Why we are doing this is: most times our binding library will build successfully, however, there will be some missing classes or interfaces which will cause the library failed to be used.

The worst case I ever met is that there are no classes and interfaces generated at all with the build succeeded.

Firstly, we need to decompiler the Android library.

  • If it’s a .jar package, directly drag into the Java Decompiler or open with it.
  • If it’s a .aar package, first extract/unzip this file, find the classes.jar file, open with Java Decompiler.

After we got the package decompilered, we can check on the source code of the Java package. Check from the below points and to see whether anything related to our Diagnostic output.

  • Are there any classes that have characteristics of obfuscation? (only lowercase letters/numbers/$) EX: a.class / a$.class
  • Are there any import statements of libraries not referenced?
  • What respective versions of dependencies does the Binding SDK use?
  • What Android API level does the .jar/.aar support?
  • What version of Java/JDK was this library compiled with?

2. Fixing issues

Choose the right AndroidClassParser

There are 2 types of AndroidClassParser that can be applied in binding project:

  1. jar2xml which uses Java reflection to extract types and members from a .jar file
  2. class-parse which parses Java bytecode directly

You can set the respective parser via the <AndroidClassParser> MSBuild property inside your csproj:

i.e.

  • <AndroidClassParser>class-parse</AndroidClassParser> - Would turn on Class Parse
  • <AndroidClassParser>jar2xml</AndroidClassParser> - Would turn on jar2xml

Note: the default method is jar2xml. But based on my testing, you will not be able to set up jar2xml in the project peoperties setting, because it will be displayed as class-parse, check the below screenshot:

Android Parser Setting

I did it by opening the .csproj file by editor such as Notepad++, then add the setting XML statement within the PropertyGroup, check below screenshot:

Android Parser Setting

Official document: https://docs.microsoft.com/en-us/xamarin/android/deploy-test/building-apps/build-process#binding-project-build-properties

Invesitigate the api.xml file

The api.xml file is typically found in the obj\Debug folder of the Bindings project. This will be an XML definition of the API at hand. This is a great starting place to see what is currently being generated, and what can be missing. It also gives a reference to other generated classes/types that can help assist you when you’re fixing Metadata.xml.

Below are some common issues that we will meet.

Missing Reference

We have below two senarios for this issue:

  • There are some missing dependencies for the jar package.
  • There is no dependency for the jar package.

Solution:

  • If the library already exists on NuGet, simply download the NuGet package to the Bindings project. (Support libraries / etc)
  • Otherwise add the missing library to your bindings project as a ReferenceJar, EmbeddedReferenceJar, or LibraryProjectZip

Java library is required

If you got the error at least one Java library is required even you already added one .jar package.

Possible reasons

This issue can be caused by that you directly build the project after adding the jar package without setting its build action. Binding project cannot assume which way to bind, with EmbeddedJar or anything else, so we have to set it manually.

Java Version Mismatch

Sometimes types will not be generated or unexpected crashes may occur because you are using either a newer or older version of Java compared to what the library was compiled with. Ensure that the JDK Version is the same or compatible with the library.

For example, if your library is built with JDK version 8 161, then JDK version 8 171 should be compatible, but JDK 7 or 9 might not.

Learn how to modify the Metadata.xml file (Very important)

The content below is really important. The reason why this is so important is because: there is still lots of difference between coding in Java and C#, and the Java library is still written by developer. We cannot be sure that everyone’s coding style will be the same. So we might have to modify some of the elements that generated by the Java library to fit the requirements for .NET library.

As we mentioned before, api.xml is the basement for us to modify the Metadata.xml file. Because it will let us know how binding project is analyzing the jar package.

Everytime we add/modify something in the Metadata.xml file, it will reflect in the api.xml file.

First we will introduce some of the basic knowledge that we need to know about how to modify the Metadata.xml file.

Common Path

The path is the routing that how we can identify a class/interface/method/parameters in the api.xml file. Please notice that we can modify almost everything on how the binding project will reflect the jar package.

  • /interface EX: /interface[@name='AuthListener']
  • /class EX: /class[@name='MapView']
  • /method EX: /method[@name='setTileSource']
  • /method(with parameters) EX: /method[@name='OnCreate' and count(parameter)=2 and parameter[1][@type='com.my.CustomActivity'] and parameter[2][@type='android.os.Bundle']]
  • /parameter EX: /parameter[@name='p0']
  • /parameter(with type) EX: /parameter[1][@type='com.my.CustomActivity']

Common name

This is used together with the Common Path. When we identify the path in the api.xml file, the name is what we want to modify as the property of that path.

  • name="managedType" EX: Java.Lang.Object
  • name="obfuscated" - Changes the obfuscation EX: true / false
  • name="managedName" - Changes the managed name EX: MyCSharpName
  • name="propertyName" - Changes the property name EX: MyPropertyName
  • name="managedReturn" - Changes the managed return type EX: Java.Lang.Object
  • name="argsType" - changes the argument type EX: MyCustomErrorEventArgs
  • name="sender" - Changes which parameter of a method should be the sender parameter when it’s mapped to an event EX: true / false
  • name="eventName" - Changes the event name EX: MyEventName

Missing Types/Obfuscated Types

Typically we will see characteristics of obfuscated types in our respective .jar/.aar libraries and we must unobfuscate them for the Bindings Generator to generate the respective C# types.

Solution:

1
<attr path="api/package[@name='{package_name}']/class[@name='{name}']" name="obfuscated">false</attr>

Duplicate Names or Normalizing Names

Sometimes you’ll run into duplicate managedNames or you might need to normalize your generated C# classes for sanity reasons.

Please note that currently there is only one circumstance will affect the dll that will be generate, and we will get the above error:

Managed Name Error

If you click into this error, you will find the generated class will be like below screenshot:

Managed Name Error Code Symptom

Solution:

1
<attr path="/api/package[@name='{package_name}']/class[@name='{name}']" name="managedName">NewManagedName</attr>

Class Visibility

Your class might not have the proper visibility for the Bindings Generator to traverse through as it does not generate bindings for non-public classes or derived classes. Typically switching the visibility to public fixes this.

Solution:

1
<attr path="/api/package[@name='{package_name}']/class[@name='{name}']" name="visibility">public</attr>

Duplicate custom EventArgs types

This issue will cause build error. You should see something as below:

1
`error CS0102: The type `Com.Google.Ads.Mediation.DismissScreenEventArgs' already contains a definition for `p0'`
Possible reasons

The most possible reason for this issue is that sometimes interfaces will have the same kind of Event, and sometimes the events’ name will be the same. In Java, the complier will deal with this kind of things, but binding generator will not.

Assume that we have 2 Java Interfaces, named MediationBannerListener and MediationInserstitialListener, they both have their own onDismissScreen and the method has the parameter name p0, at this moment the binding generator will create 2 DismissScreenEventArgs with the same signature. This will cause the build error.

1
2
3
4
5
6
7
public interface MediationBannerListener{
void onDismissScreen(MediationBannerAdapter p0);
}

public interface MediationInterstitialListener{
void onDismissScreen(MediationInterstitialAdapter p0);
}
Solution

This is actually a by-design issue, because Java will avoid the long-term naming conversation. To resolve the issue, we still have to modify the Metadata.xml file as below:

1
2
3
4
5
6
7
<attr path="api/package[@name='com.google.ads.mediation']/
interface[@name='MediationBannerListener']/method[@name='onDismissScreen']"
name="argsType">BannerDismissScreenEventArgs</attr>

<attr path="api/package[@name='com.google.ads.mediation']/
interface[@name='MediationInserstitialListener']/method[@name='onDismissScreen']"
name="argsType">IntersitionalDismissScreenEventArgs</attr>

Class does not implement interface method

We all know in C#, if you have an interface, and some class inherits from this interface. Then this class has to implement all the methods within that interface. Otherwise there will be compiler error.

But in binding project, sometimes we will see that the method implementation does exist in the api.xml file, but you might still get the below kind of error:

1
obj\Debug\generated\src\Oauth.Signpost.Basic.HttpURLConnectionRequestAdapter.cs(8,23): error CS0738: 'Oauth.Signpost.Basic.HttpURLConnectionRequestAdapter' does not implement interface member 'Oauth.Signpost.Http.IHttpRequest.Unwrap()'. 'Oauth.Signpost.Basic.HttpURLConnectionRequestAdapter.Unwrap()' cannot implement 'Oauth.Signpost.Http.IHttpRequest.Unwrap()' because it does not have the matching return type of 'Java.Lang.Object'
Possible reason

This issue actually is caused by the covariant return type which used to bind the Java method. In the above error message, the method Oauth.Signpost.Http.IHttpRequest.UnWrap() needs to return Java.Lang.Object. But our binding generator determines that it should return HttpURLConnection type.

Solution
  • Add a definition of a partial class, named HttpURLConnectionRequestAdapter, and then explictly implement the method IHttpRequest.UnWrap(), as below code:

    1
    2
    3
    4
    5
    6
    7
    namespace Oauth.Signpost.Basic{
    partial class HttpURLConnectionRequestAdapter{
    Java.Lang.Object OauthSignpost.Http.IHttpRequest.Unwrap(){
    return Unwrap();
    }
    }
    }
This is actually uses C# .NET feature to avoid the issue. No matter what the binding generator got for us, we just implement a new method implementation that meets the requirement, in this case, return type is `Java.Lang.Object`. Since the class is partial, so this method will be combined with the one that binding generator got for us.
  • Remove the covariant feature here. Means that we modify the Metadata.xml file to change the return type of this method.

    1
    2
    3
    4
    <attr
    path="api/package[@name='oauth.signpost.basic']/class[@name='HttpURLConnectionRequestAdapter']
    /method[@name='unwrap']" name="managedReturn">Java.Lang.Object
    </attr>

Adding Types

You can use <add-node> to add just about anything to your binding which will generate in the api.xml file. Typically you may want to add a class, change a constructor, or switch a generic type.

EX: (Creates a class with a constructor and field):

1
2
3
4
5
6
<add-node path="api/package[@name='{org.alljoyn.bus}']">
<class abstract="false" deprecated="not deprecated" final="false" name="AuthListener.AuthRequest" static="true" visibility="public" extends="java.lang.Object">
<constructor deprecated="not deprecated" final="false" name="AuthListener.AuthRequest" static="false" type="org.alljoyn.bus.AuthListener.AuthRequest" visibility="public" />
<field name="p0" type="org.alljoyn.bus.AuthListener.Credentials" />
</class>
</add-node>

Removing Types

Typically it’s easiest to just remove anything in a binding that we will not use. You can look at the class that you want to use and see everything it references to get a better idea of what you will need and what you will not.

Solution:

1
<remove-node path="api/package[@name='{package_name}']/class[@name='{name}']" />

Common Metadata Fixes

These are some samples from GitHub that let us know how to resolve some Java library binding issues.

Binding ADTECH Mobile

Binding Brother Print SDK for Android

Binding NeoReaderSDK

Binding Java WebSocket

Binding Socialize Android SDK

Using Java Annotations

  1. Be sure to [Export] the respective Method/Class/etc.
  2. Also ensure you reference Mono.Android.Export in your Xamarin.Android Project

Java.Interop.ExportAttribute Class


3. Terms

JNI (Java Native Interface)

In computing, the Java Native Interface (JNI) is a programming framework that enables Java code running in a Java Virtual Machine (JVM) to call and be called by native applications (programs specific to a hardware and operating system platform) and libraries written in other languages such as C, C++ and assembly.

Android Callable Wrappers (ACW)

Android callable wrappers are a JNI bridge that are used whenver the Android runtime needs to invoke managed code.

Managed Callable Wrappers (MCW)

Managed callable wrappers are a JNI bridge that are used whenever managed code needs to invoke Android code and provide support for overriding virtual methods and implementing Java interfaces.

Embedded vs. Non-Embedded

When using a Build Action such as EmbeddedJar or EmbeddedReferenceJar, it will embed the respective library into the .apk so it will be available at runtime. Otherwise it is expected that either the Device or the application will provide the .jar at runtime. (I.E. It is already loaded on device or will be provided via a download/etc)

Reference vs. Non-Reference

When using a Build Action such as ReferenceJar or EmbeddedReferenceJar, it will not generate Manage Callable Wrappers(ACW) and will not be exposed to the client.

Java is not the same as C#

Because of this limitation, you will need to be aware of the respective generated C# code as there might be certain things that the languages handle differently.

EX: Java -> C#

  • get/set methods -> properties
  • fields -> properties * listeners -> events
  • static nested class -> nested class
  • inner class -> nested class with an instance constructor

4. Conclusion

Although Xamarin.Android Binding errors might be confusing and the JNI might be intimidating, there is always a few ways to work around the issue at hand. Documentation:

Binding a Jar

Binding a Library Project

Java Bindings Metadata

Mono Metadata

Creating Bindings Using Metadata

Naming Parameters With Javadoc

Troubleshooting

XPath Tutorial

XPath Functions

Xamarin Univeristy Course:

https://university.xamarin.com/classes/track/xamarin-android#and450-binding

How to use Bot Service in Xamarin.Forms project (WebView version) Enable Swipe Gesture in XF for iOS when Navigation Bar hidden
You need to set install_url to use ShareThis. Please set it in _config.yml.

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×