Java bindings

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 at http://tinyurl.com/GoogleAnalyticsForAndroid. At the time of writing, the version of the Android SDK was 3.01, so some of these instructions may change over time.

Let's begin creating a Java binding as follows:

  1. Start a new Android Java Bindings Library project in Xamarin Studio. You may use the same solution as we did for iOS if you wish.
  2. Name the project GoogleAnalytics.Droid.
  3. Add libGoogleAnalyticsServices.jar from the Android SDK to the project under the Jars folder.
  4. Build the project. You will get a few errors, which we'll address in a moment.

Most of the time you spend working on Java bindings will be to fix small issues that prevent the generated C# code from compiling. But 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 may run into:

  • Java obfuscation: If the library is run through an obfuscation tool such as ProGuard, the class and method names may not be valid C# names.
  • Covariant return types: Java has different rules for return types than C# does. A class can override a method from its subclass and change the return type to a subclass of that type. For this reason, you may need to modify the return type for the generated C# code to compile.
  • Visibility: The rules that Java has for accessibility are different from those of C#; the visibility of methods in subclasses can be changed. Sometimes you will have to change visibility in C# to get it to compile.
  • Naming collisions: Sometimes the C# code generator can get things a bit wrong and generate two members or classes with the same name.
  • Java generics: The use of generic classes in Java can often cause issues in C#.

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 may look a bit strange, which is because it is using an XML matching query language named XPath. In general, just think of it as pattern - matching for XML. For full documentation on XPath syntax, check out some of the many resources online such as http://w3schools.com/xpath.

You may 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. Click on Display Options | Show All Files. Open api.xml under the obj/Debug folder. This is the Java definition file that describes all types and methods within the Java library. If you notice, the XML here directly correlates to the XPath expressions we'll be writing.

For 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 the C# code necessary for calling into the Java library.

Add the following declarations in Metadata.xml:

<remove-nodepath="/api/package[@name='com.google.analytics.containertag.common']" />
<remove-nodepath="/api/package[@name='com.google.analytics.containertag.proto']" />
<remove-nodepath="/api/package[@name='com.google.analytics.midtier.proto.containertag']" />
<remove-nodepath="/api/package[@name='com.google.android.gms.analytics.internal']" />
<remove-nodepath="/api/package[@name='com.google.android.gms.common.util']" />
<remove-nodepath="/api/package[@name='com.google.tagmanager']" />
<remove-nodepath="/api/package[@name='com.google.tagmanager.proto']" />
<remove-nodepath="/api/package[@name='com.google.tagmanager.protobuf']" />

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:

<classabstract="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:

<attrpath="/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 would be as follows:

<classabstract="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:

<attrpath="/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 and looking at the Register attribute, as shown in the following screenshot:

Java bindings

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

<methodabstract="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 for 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 andparameter[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 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 could 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 proper name for each parameter. So, Xamarin.Android does the best thing it can and automatically names 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>

Upon rebuilding the binding project, this will effectively rename the parameters for these four methods in the EasyTracker class. At this time, I would recommend going 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 to check out Xamarin's documentation site at http://docs.xamarin.com/android. There are certainly more complicated scenarios than what we ran into for creating a binding for the Google Analytics library.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.131.62