I am using VS2015 Update3 with current/up-to-date Xamarin, and am trying to create a binding library to wrap a 3rd party SDK written in Java. I have a C#/.Net background, with little Java experience thus far. This SDK depends on 3 other items, which are available as AAR's or via NuGet.
I was able to create a binding library for the parent AAR, with some MetaData.xml tweakery to adjust parameter types and return types and correct a class accessor to satisfy its abstract base class, all of which were initially preventing the resulting C# .Net Android binding library from building. Now the parent binding assembly builds, but can't execute, as it requires the other 3 AAR dependencies.
Dependencies needed ("#" is the alias for each when mentioned in the post below):
In this scenario, if I need to create a binding dll to wrap a parent AAR that depends on 3 other AARs:
Should I (A) create 4 binding assemblies - a parent binding assembly Bind_AAR_1.dll, that depends on Bind_AAR_2.dll, Bind_AAR_3.dll, and Bind_AAR_4.dll?
OR, should I (B) dig into the 2, 3, and 4 AAR files and get the JAR file from each, and include them in Bind_AAR_1.dll directly in the Jars folder using 'EmbeddedJar' (or other attribute/setting)?
OR, seeing that 2, 3, and 4 are actually availabe via NuGet, should I (C) reference them into the Bind_AAR_1 project using NuGet? Will the Javacode in the AAR 1 be able to see the code in AAR's 2, 3, and 4 if I use NuGet to reference/include them?
I've tried variations on each of methods (A), (B), and (C) with no luck yet, or resulting work that leads to more work, which leaves me wondering what is the best/right way to do this.
On a related topic, is it a best practice to use the Metadata.xml file to explicitly remove everything (classes, etc) from the resulting generated wrapper API that you do not directly plan to use in your Xamarin project? Seems like it might be good to limit the interface and perhaps also reduce the build size (and possibly build time) of the binding dll.
Any help would be appreciated!
Curtis
=======================================================================
4/11/2017 - Update
Very glad to have input from the likes of @_JonDouglas! but... still not able to get this SDK working.
I have tried each of these methods A, B, and C, and have been able to get my bindings project to build (with manipulations done via transforming/MetaData.xml). The 3rd party SDK was designed/documented to be a part of / a component in a Java solution. Per the vendor's instructional documentation, this solution would have the 3 dependencies added to its .gradle file, thus providing the reference to these items in the overall Java solution. I have no Java history, so winging it here.
As it is, when I wrap the SDK (alone or with dependencies using any of the 3 methods) with Visual Studio into a Xamarin Android Binding Assembly, at runtime the SDK hits ClassNotFound exceptions (evidenced by "Google Play not found." in the logs). The classes used from the required Java dependencies are created created in a late bound fashion using name strings for classnames like this:
try
{
Class.forName("com.google.android.gms.common.GooglePlayServicesUtil");
It doesn't seem to matter how I build the binding assembly in Visual Studio using any of the 3 methods above to include the dependency items (1 AAR + 3 Nuget OR 1 AAR + 3 Jars in 1 binding OR 4 AARs in 4 bindings using project references). At runtime, the SDK doesn't seem to see them. Perhaps that makes sense that it won't - there's no .gradle (like a csproj file) that points to them in the SDK itself?
//more code here
}
catch (ClassNotFoundException e)
{
Log.e("3rd Party SDK", "Google Play not found.");
}
I am thinking what I need to do is (closer to the vendor's instruction) create a Java solution complete with a .gradle file that includes the SDK as well as the 3 dependencies in the Java project, building to create an AAR file. Then, I believe I may be able to create a Xamarin binding assembly for the resulting encompasing AAR. Then I'll add a reference to this binding assembly to my .Andriod assembly within my solution and proceed.
Theory (yet to be proven): [Failed. See 4/12 update below.] Wrap the Java SDK + its coded dependencies (explicit in the .gradle) in a module/library, and then wrapping that in a Xamarin Android Binding library. Then all I'll need to do is figure out what public methods I need to use (and therefore expose) from this wrapper module/library, add these public methods and forward the calls to the SDK.
Big Picture: I'm trying to design and code this as a cross-platform Xamarin solution, but am working with an Android-specific and IOS-specific SDK that appear both designed to be part of the application/UI experience (designed to do more than just handle communication to the 3rd party). This UI aspect has yet to play out, as I've not seen either SDK function as of yet.
=======================================================================
4/12/2017 - Update
Theory (FAILED): Wrap the Java SDK + its coded dependencies (explicit in the .gradle) in a module/library, and then wrap that in a Xamarin Android Binding library. We coded, built, deployed, and tested this. I now see that the app at runtime gets into the first layer of Java code, but fails to find any referenced code lower than that (aka dependencies). The wrapper's code runs fine but the wrapper at runtime can't find the SDK's classes, nor even a simple Java test component that we attempted to use via the wrapper. This is equivalent to issue we had with the SDK in the first place when we simply created a bindings file for it alone, and referenced the rest into the binding assembly by JAR or via NuGet. The lower level code is still not found, only the first layer of java executes. Still trying to come up with a solution.
=======================================================================
4/13/2017 - Update
I have made some progress. One step forward, and maybe two steps back as they say.
I now realize that the 3rd party SDK needs more of Google Play Services than just the two dependencies. I decompiled the two items explicitly mentioned, and the named class is not in there. I started looking at the local Android SDK location (as configured in Visual Studio) and found 'play-services-6.5.87.AAR'. Including this in my binding assembly as an EmbeddedResourceJar finally got the SDK code to find the particular class listed in code above.
I am questioning the value of the Java wrapper described above, since everything needed for dependencies is now included as an EmbeddedReferenceJar. Seems like I should just bind the SDK AAR directly (with no added Java wrapper layer). I am rethinking this and trying to just create the binding assembly pointed to the SDK AAR in the JARS folder, and then include Google Play Services using NuGet.
Proceeding with that, (using NuGet to reference Google Play Services) I'm now getting this error:
No resource found that matches the given name (at 'value' with value '@integer/google_play_services_version'). {project name} {project path}\AndroidManifest.xml
I've tried a LOT of different things to try to get rid of this build-time error, but have not had any luck.
If I stick with using the JAR file from inside Google's 'play-services-6.5.87.AAR' as an embeddedResourceJar, I don't get the build errors, but I do get runtime errors that the SDK can't find Android manifest settings needed. If you have a project arranged like this, am I correct as to which Android Manifest is used?
XXX PCL Project
--> XXX.Android project <-- My guess is that this is where the AndroidManifest.XML file is located that will be used....?
----> XXX.Android.AARBinding project
------> JARS folder --> 3pVendorSDK.AAR
Still digging....
Since my solution is to be Cross-Platform, I have:
The respective .IOS projects/assemblies will follow this naming pattern, when we get to that.
Manifest
References
Components (NuGet)
Assets
Build Time Additional work needed:
As stated above, when I built MyApplication.Android.Binding, it built fine on its own. When I referenced it in MyApplication.Android, though, it generated a new Java code translation build error about the scope of a particular class not being as expected/required. To address this, I went back to the Binding assembly’s MetaData.xml file and chose to remove the problem class from the generated Java code interface since I did not have plans to call or use.
TIP: The easiest way to do this (that I've found) in Visual Studio is to enable the Show All Files feature on the BindingAssembly project, and then look in the obj\Debug\generated\src\ folders for the problem class. Once you find it, click on it and the generated Java interface code has the XPath statements generated as comments. These are what you need to either alter or remove the problem classes or methods using the MetaData.XML file.
Now all I need to do is write my cross-platform code on top (using Dependency Inversion + Xamarin Forms built-in DependencyService to reach each SDK at runtime) and my app should be good to go!
Hopefully these findings are helpful to others who need to work through a similar binding scenario.
If you do see any content here that is incorrect, or if you have anything you'd like to add, please comment!
You should be able to do #3 which you simply reference the pre-built NuGet packages of these existing bindings of GPS/support libraries.
Please make sure that you use the exact version the binding library expects as google moves classes all the time.
My personal recommendation is the following in order of difficulty (easy -> hard):
EmbeddedReferenceJar
/ ReferenceJar
of the .jar
or classes.jar
(Use a LibraryProjectZip
if there are resoruces)As a best practice, yes you should use <remove-node>
on items that you will not use. However this is mainly used when errors are being generated by code you won't use in the first place. I would just keep the items around regardless here unless there are quite a bit of methods you will never touch so you can be mindful of your dexcount.
For further help you can view my bindings guide: https://gist.github.com/JonDouglas/dda6d8ace7d071b0e8cb
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With