In the same manner as iOS, Xamarin has provided full support for calling into Java libraries from C# with Xamarin.Android
. The native Android SDKs function in this way and developers can leverage the Android Java Bindings
project to take advantage of other native Java libraries in C#. The main difference here is that not a lot has to be done by hand in comparison to Objective-C bindings. The Java syntax is very similar to that of C#, so many mappings are exactly one-to-one. In addition, Java has metadata information included with its libraries, which Xamarin uses to automatically generate the C# code required for calling into Java.
As an example, let's make a binding for the Android version of the Google Analytics SDK. Before we begin, download the SDK from http://tinyurl.com/GoogleAnalyticsForAndroid. At the time of writing, the version of the Android SDK 3.01, so some of these instructions might change over time.
Let's begin creating a Java binding as follows:
Android Java Bindings Library
project in Xamarin Studio. You can use the same solution as we did for iOS if you wish.GoogleAnalytics.Droid
.libGoogleAnalyticsServices.jar
from the Android SDK to the project under the Jars
folder. By default, the build action for the file will be EmbeddedJar
. This packs the jar file into the DLL, which is the best option for ease of use.Most of the time you spend working on Java bindings will be to fix small issues that prevent the generated C# code from compiling. Don't fret; a lot of libraries will work on the first try without having to make any changes at all. Generally, the larger the Java library is, the more work you have to do to get it working from C#.
The following are the types of issues you might run into:
So before we get started on solving these issues in our Java binding, let's first clean up the namespaces in the project. Java namespaces are of the form com.mycompany.mylibrary
by default, so let's change the definition to match C# more closely. In the Transforms
directory of the project, open Metadata.xml
and add the following XML tag inside the root metadata node:
<attr path="/api/package[@name='com.google.analytics.tracking .android']" name="managedName">GoogleAnalytics.Tracking</attr>
The attr
node tells the Xamarin compiler what needs to be replaced in the Java definition with another value. In this case, we are replacing managedName
of the package with GoogleAnalytics.Tracking
because it will make much more sense in C#. The path value might look a bit strange, which is because it uses an XML matching query language named XPath. In general, just think of it as a pattern matching query for XML. For full documentation on XPath syntax, check out some of the many resources online such as http://w3schools.com/xpath.
You might be asking yourself at this point, what is the XPath expression matching against? Return to Xamarin Studio and right-click on the solution at the top. Navigate to Display Options | Show All Files. Open api.xml
under the obj/Debug
folder. This is the Java definition file that describes all the types and methods within the Java library. If you notice, the XML here directly correlates to the XPath expressions we'll be writing.
In our next step, let's remove all the packages (or namespaces) we don't plan on using in this library. This is generally a good idea for large libraries since you don't want to waste time fixing issues with parts of the library you won't even be calling from C#. Note that it doesn't actually remove the Java code; it just prevents the generation of any C# declarations for calling it from C#.
Add the following declarations in Metadata.xml
:
<remove-node path="/api/package[@name='com.google.analytics .containertag.common']" /> <remove-node path="/api/package[@name='com.google.analytics .containertag.proto']" /> <remove-node path="/api/package[@name='com.google.analytics .midtier.proto.containertag']" /> <remove-node path="/api/package[@name='com.google.android .gms.analytics.internal']" /> <remove-node path="/api/package[@name='com.google.android .gms.common.util']" /> <remove-nodepath="/api/package[@name='com.google.tagmanager']" /> <remove-node path="/api/package[@name='com.google.tagmanager.proto']" /> <remove-node path="/api/package[@name='com.google.tagmanager.protobuf.nano']" />
Now when you build the library, we can start resolving issues. The first error you will receive will be something like the following:
GoogleAnalytics.Tracking.GoogleAnalytics.cs(74,74): Error CS0234: The type or namespace name 'TrackerHandler' does not exist in the namespace 'GoogleAnalytics.Tracking'. Are you missing an assembly reference?
If we locate TrackerHandler
within the api.xml
file, we'll see the following class declaration:
<class abstract="true" deprecated="not deprecated" extends="java.lang.Object" extends-generic-aware="java.lang.Object" final="false" name="TrackerHandler" static="false" visibility=""/>
So, can you spot the problem? We need to fill out the visibility
XML attribute, which for some reason is blank. Add the following line to Metadata.xml
:
<attr path="/api/package[@name='com.google.analytics .tracking.android']/class[@name='TrackerHandler']" name="visibility">public</attr>
This XPath expression will locate the TrackerHandler
class inside the com.google.analytics.tracking.android
package and change visibility
to public
.
If you build the project now, it will complete successfully with one warning. In Java binding projects, it is a good idea to fix warnings since they generally indicate that a class or method is being omitted from the binding. Notice the following warning:
GoogleAnalytics.Droid: Warning BG8102: Class GoogleAnalytics.Tracking.CampaignTrackingService has unknown base type android.app.IntentService (BG8102) (GoogleAnalytics.Droid)
To fix this issue, locate the type definition for CampaignTrackingService
in api.xml
, which is as follows:
<class abstract="false" deprecated="not deprecated" extends="android.app.IntentService" extends-generic-aware="android.app.IntentService" final="false" name="CampaignTrackingService" static="false" visibility="public">
The way to fix the issue here is to change the base class to the Xamarin.Android
definition for IntentService
. Add the following code to Metadata.xml
:
<attr path="/api/package[@name='com.google.analytics .tracking.android']/class[@name='CampaignTrackingService']" name="extends">mono.android.app.IntentService</attr>
This changes the extends
attribute to use the IntentService
found in Mono.Android.dll
. I located the Java name for this class by opening Mono.Android.dll
in Xamarin Studio's Assembly Browser. Let's take a look at the Register
attribute, as shown in the following screenshot:
To inspect the *.dll
files in Xamarin Studio, you merely have to open them. You can also double-click on any assembly in the References
folder in your project.
If you build the binding project now, we're left with one last error, which is as follows:
GoogleAnalytics.Tracking.CampaignTrackingService.cs(24,24): Error CS0507: 'CampaignTrackingService.OnHandleIntent(Intent)': cannot change access modifiers when overriding 'protected' inherited member 'IntentService.OnHandleIntent(Android.Content.Intent)' (CS0507) (GoogleAnalytics.Droid)
If you navigate to the api.xml
file, you can see the definition for OnHandleIntent
as follows:
<method abstract="false" deprecated="not deprecated" final="false" name="onHandleIntent" native="false" return="void" static="false" synchronized="false" visibility="public">
We can see here that the Java method for this class is public
, but the base class is protected
. So, the best way to fix this is to change the C# version to protected
as well. Writing an XPath expression to match this is a bit more complicated, but luckily Xamarin has an easy way to retrieve it. If you double-click on the error message in the Errors pad of Xamarin Studio, you'll see the following comment in the generated C# code:
// Metadata.xml XPath method reference: path="/api/package[@name='com.google.analytics .tracking.android']/class[@name='CampaignTrackingService'] /method[@name='onHandleIntent' and count(parameter)=1 and parameter[1][@type='android.content.Intent']]"
Copy this value to path
and add the following to Metadata.xml
:
<attr path="/api/package[@name='com.google.analytics .tracking.android']/class[@name='CampaignTrackingService'] /method[@name='onHandleIntent' and count(parameter)=1 and parameter[1][@type='android.content.Intent']]" name="visibility">protected</attr>
Now, we can build the project and get zero errors and zero warnings. The library is now ready for use within your Xamarin.Android
projects.
However, if you start working with the library, notice how the parameter names for the methods are p0
, p1
, p2
, and so on. Here are a few method definitions of the EasyTracker
class:
public static EasyTracker GetInstance(Context p0); public static void SetResourcePackageName(string p0); public virtual void ActivityStart(Activity p0); public virtual void ActivityStop(Activity p0);
You can imagine how difficult it would be to consume a Java library without knowing the proper parameter names. The reason the parameters are named this way is because the Java metadata for its libraries does not include the information to set the correct name for each parameter. So, Xamarin.Android
does the best thing it can and autonames each parameter sequentially.
To rename the parameters in this class, we can add the following to Metadata.xml
:
<attr path="/api/package[@name='com.google.analytics .tracking.android']/class[@name='EasyTracker'] /method[@name='getInstance']/parameter[@name='p0']" name="name">context</attr> <attr path="/api/package[@name='com.google.analytics .tracking.android']/class[@name='EasyTracker'] /method[@name='setResourcePackageName']/parameter[@name='p0']" name="name">packageName</attr> <attr path="/api/package[@name='com.google.analytics .tracking.android']/class[@name='EasyTracker'] /method[@name='activityStart']/parameter[@name='p0']" name="name">activity</attr> <attr path="/api/package[@name='com.google.analytics .tracking.android']/class[@name='EasyTracker'] /method[@name='activityStop']/parameter[@name='p0']" name="name">activity</attr>
On rebuilding the binding project, this will effectively rename the parameters for these four methods in the EasyTracker
class. At this time, I would recommend that you go through the classes you plan on using in your application and rename the parameters so that it will make more sense to you. You might need to refer to the Google Analytics documentation to get the naming correct. Luckily, there is a javadocs.zip
file included in the SDK that provides HTML reference for the library.
For a full reference on implementing Java bindings, make sure you check out Xamarin's documentation site at http://docs.xamarin.com/android. There are certainly more complicated scenarios than what we ran into when creating a binding for the Google Analytics library.
3.143.235.23