Skip to content

Instantly share code, notes, and snippets.

@JonDouglas
Last active September 8, 2024 20:45
Show Gist options
  • Save JonDouglas/dda6d8ace7d071b0e8cb to your computer and use it in GitHub Desktop.
Save JonDouglas/dda6d8ace7d071b0e8cb to your computer and use it in GitHub Desktop.
Xamarin Android Bindings Troubleshooting

Approaching a Xamarin.Android Bindings Case

1. Investigation

One of the best ways to investigate a problematic Xamarin.Android Binding is to first ensure you have the proper tooling available:

After you have all of your tools available, you can take a preliminary look at the problem at hand.

  1. Build the problematic Bindings project
  2. Get the full Diagnostic Build Log
  3. Examine the error (You may need to look through each Build Task to get a further idea of where the problem lies)

You now have the diagnostic build output, which should already give us clues to the problem at hand. Now we can do some investigation in the Android Library(.aar/.jar) and various documentation.

First, let's decompile the Android Library

  • If it's a .jar, simply drag/open the .jar in a Java Decompiler
  • If it's an .aar, you can extract/unzip the archive and find the classes.jar file to open in a Java Decompiler

Now it's time for an initial look. Do you see anything out of the ordinary or closely related to the error message at hand?

Here are a couple of things that come to mind:

  • 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

Pick your tooling

There are now two different AndroidClassParser available to use with binding projects:

  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: If you do not specify a value, it will default to class-parse as it's more stable and feature rich.

https://developer.xamarin.com/guides/android/under_the_hood/build_process/#Binding_Project_Build_Properties

Investigate 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.

Missing References

  • 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 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.

Common Paths

  • /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 Names

  • 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.

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

See Common Paths for more types

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.

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

See Common Paths for more types

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.

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

See Common Paths for more types

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):

  <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.

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

See Common Paths for more types

Common Metadata Fixes

https://gist.github.com/brendanzagaeski/c32d65c21e152799af69

https://gist.github.com/brendanzagaeski/6d1052a8b76f9067548e

https://gist.github.com/brendanzagaeski/69f490e31ca6a71136ff

https://gist.github.com/brendanzagaeski/3868e30b85a1feb1181b

https://gist.github.com/brendanzagaeski/9607158

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

https://developer.xamarin.com/api/type/Java.Interop.ExportAttribute/

XPath Customization

You can use up to XPath 1.0 features within Metadata.xml transformations. There are quite a few great resources out there for explaining different functions, operators, etc.

Mono Recommended 1

Mono Recommended 2

Mono Recommended 3

There are other general tutorials on XPath that you can find here.

XPath Tutorial

XPath Functions

Quick examples:

 <!-------Remove nodes in @name that match com.example.internal----->
<remove-node path="/api/package[starts-with (@name, 'com.example.internal')]" />

 <!-------Remove nodes in @name that contain the string com.example.internal----->
<remove-node path="/api/package[contains (@name, 'com.example.internal')]" />

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

@mzekrallah
Copy link

awesome bro .. so helpful .. thanks a lot for sharing ..

@jzeferino
Copy link

good job @JonDouglas.

@software770-2
Copy link

Yes, but what do you do with:
Severity Code Description Project File Line Suppression State
Error CS0738 'JsonStreamParser' does not implement interface member 'IIterator.Next()'. 'JsonStreamParser.Next()' cannot implement 'IIterator.Next()' because it does not have the matching return type of 'Object'. Binding_Gson C:\Users\ymaza\source\repos\PaySafeSdk_CSharp_Android\PaySafeSdk_CSharp_Android\AndroidBindings\Binding_Gson\obj\Debug\generated\src\Com.Google.Gson.JsonStreamParser.cs 9 Active

This is simple Google Gson 1.7.1 and probably the class does redeclar functions drom the super wich is forbeaten in c# and who has the time to debug Google classes... Any idea ?

Thank you !

@dehghani-mehdi
Copy link

Can someone explain how to fix ambiguous errors?

@JonDouglas
Copy link
Author

@dehghani-mehdi Can you be a little more specific with a sample error? In most cases, you would either remove or rename one.

Copy link

ghost commented May 21, 2018

@dehghani-mehdi same name class name in different name spaces. this error exist if you directly call the name of the class

.
e.g string abc= new string();

you need to call it Like.

e.g System.string abc = new System.string();

@u10354167
Copy link

Hi Jon Douglas
I hope you may help me. I'm trying to bind but my build have the following errors
CS0102 and CS0111
CS0111 Type 'ErrorEventArgs' already defines a member called '.ctor' with the same parameter types
CS0102 The type 'ErrorEventArgs' already contains a definition for 'p0'

I tried to add managedName to the metadata but it seems not to have any effects

@kibotu
Copy link

kibotu commented Jun 5, 2018

Well done, this post helped me on more than on occasion. Perhaps you could add some infos to the code generation target (XamarinAndroid or XAJavaInterop1)
cheers

@ydniw
Copy link

ydniw commented Jun 20, 2018

F***ng AWESOME!!!!

@acaliaro
Copy link

acaliaro commented Jul 29, 2018

@JonDouglas, I have the same ".ctor" and "p0" problem. There is a solution for this?

@phanigit
Copy link

This is awesome, thanks @JonDouglas.

I've created a java bindings for our SDK successfully. However, I'm not able to find a way to generate the API reference doc similar to Javadoc automatically by pulling the content that exists in Java files. I've tried using mdoc and able to generate structurally working API doc but all the content seems to be stubbed out (filled with "Documentation for this section has not yet been entered"). Any pointers on how to populate the content automatically?

Thanks!

@FranciscoG
Copy link

I'm creating a binding for the first time (from Java) and I'm running into a lot of weird syntax related errors.
Most of the errors look like this: Error CS1003: Syntax error, ',' expected (CS1003)

I have access to the original Java files so I don't need to decompile the JARs (it builds .aar files atually).

I can't really find much help on why this happens, any clues?

@dehghani-mehdi
Copy link

@JonDouglas I tried to bind okio library using its jar file, I got so many error, I fixed some of them, I don't know why, but I got - at the beginning on almost all class names (e.g: -Base64), how I can fix this?

@JohnGaby
Copy link

JohnGaby commented Sep 9, 2019

I am trying to bind Amazons Mobil Ads jar file. I get three errors when I do so all of which are missing interface declarations. For example the interface IAdControlCallback is missing. Looking in the jar file using the decompiler however I see the interface AdControllCallback defined. Also looking in the api.xml file that is generated I also see an entry for AdControllerCallback. What would I need to do to fix this issue?

@requestspersecond
Copy link

requestspersecond commented Sep 25, 2019

@JohnGaby I'm having the exact same problem. My online readings suggested it might be a visibility issue, so I have tried:

<attr path="/api/package[@name='com.Amazon.Device.Ads']/class[@name='InterstitialAd']/field[@name='iadcontrolcallback']" name="name">IAdControlCallback</attr>

That's not working because the path, case and/or syntax isn't correct.

Here are the three errors I'm getting:
.../obj/Debug/generated/src/Com.Amazon.Device.Ads.AmazonViewableAdSDKBridgeFactory.cs(114,114):
Error CS0234: The type or namespace name 'IAdSDKBridgeFactory' does not exist in the namespace 'Com.Amazon.Device.Ads'
(are you missing an assembly reference?) (CS0234)

.../obj/Debug/generated/src/Com.Amazon.Device.Ads.AdLayout.cs(108,108):
Error CS0234: The type or namespace name 'IAdControlCallback' does not exist in the namespace 'Com.Amazon.Device.Ads'
(are you missing an assembly reference?) (CS0234)

.../obj/Debug/generated/src/Com.Amazon.Device.Ads.InterstitialAd.cs(112,112):
Error CS0234: The type or namespace name 'IAdControlCallback' does not exist in the namespace 'Com.Amazon.Device.Ads'
(are you missing an assembly reference?) (CS0234)

Things I have tried:

  1. Created a Hello World app in Android Studio.
    Added amazon-ads-5.9.0.jar.
    Compiled with no errors.
    Called AdRegistration.setAppKey("0123456789ABCDEF0123456789ABCDEF");
    Ran app. No errors.

  2. Used Android studio to create an .aar file, per this guy's suggest: https://www.youtube.com/watch?v=PcSc0jV9Rh4
    Added to a Xamarin Binding project.
    Fails to compile with the aforementioned errors.

  3. Added amazon-ads-5.9.0.jar. to the Xamarin Binding project.
    Fails to compile with the aforementioned errors.

If anyone has any suggestions, it would be most appreciated. It's the last item on the todo list to release the app. Client has been patient, but time is running out.

Cheers.

Rick

@JohnGaby
Copy link

Actually, I was able to get to work eventually and it was a visibility issue. However, when I tried to use it, I always got a 'no fill' error returned from Amazon. I tried to contact them about it, but they said they do not support Xamarin and refused to help. It really didn't look like a problem with the Xamarin bindings however so I downloaded Amazon's Android example and built it using Android Studio. I was not surprised that it also returned 'no fill' to all my load requests. I then contacted Amazon again and they are looking into it (supposedly) but I still don't have a solution.

@JohnGaby
Copy link

I believe I have it working now and can load interstitial ads using Xamarin. If you would like, I can give you the project that I used to create the bindings dll.

@requestspersecond
Copy link

Yes, that would be most appreciated. consulting@requestspersecond.com

@JohnGaby
Copy link

I created a github project: https://github.com/JohnGaby/AmazonAdsBindings

@requestspersecond
Copy link

requestspersecond commented Sep 26, 2019 via email

@vvydier
Copy link

vvydier commented Feb 12, 2020

How do I incorporate a gradle plugin jar in the binding project, that is used to inject additional info (via byte code injection) into a class before building the Xamarin Android project?

@mdmota
Copy link

mdmota commented Mar 24, 2020

I need to call an activity that is on an Android lib from a Depedency Service Xamarin Droid.

I generated the AAR for my Android studio project, but it is not possible to access an activity from the Android linking library

To access a simple class ok.
To call an activity no.

Can someone help me?

Attached the Android studio and Xamarin projects
https://drive.google.com/drive/folders/14nTy0O7thgbZtDw91vGKjyjFsq6r8uyE?usp=sharing

@redradist
Copy link

Have you faced with the following issue https://forums.xamarin.com/discussion/184666/xamarin-java-binding-library-multiple-already-defined-errors#latest ?

I cannot figure out why for such simple code generator cannot generate proper library ... ?!

@jpobst
Copy link

jpobst commented Apr 1, 2021

I've been incorporating this great resource with additional information on the Java.Interop wiki:
https://github.com/xamarin/java.interop/wiki/Troubleshooting-Android-Bindings-Issues

@carrilloan
Copy link

Awesome. Is there a way to exclude resources when doing an aar binding?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment