Bug 18154 - Binding Error:java.lang.NoClassDefFoundError: com/google/android/gms/maps/GoogleMap$OnCameraChangeListener
Summary: Binding Error:java.lang.NoClassDefFoundError: com/google/android/gms/maps/Goo...
Status: RESOLVED INVALID
Alias: None
Product: Compilers
Classification: Mono
Component: Other ()
Version: unspecified
Hardware: PC Windows
: --- normal
Target Milestone: ---
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2014-03-04 09:06 UTC by ole.kristensen
Modified: 2014-03-13 10:38 UTC (History)
3 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:


Attachments
Eclipse and VS screen dump and binding log (310.61 KB, application/vnd.openxmlformats-officedocument.wordprocessingml.document)
2014-03-04 09:06 UTC, ole.kristensen
Details
New binding log. Without dll reference to google play service dll (85.83 KB, text/plain)
2014-03-04 13:50 UTC, ole.kristensen
Details


Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on GitHub or Developer Community with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED INVALID

Description ole.kristensen 2014-03-04 09:06:58 UTC
Created attachment 6209 [details]
Eclipse and VS screen dump and binding log

Not all classes are bound when binding google map utility and google-play-services together.

I have working project in Eclipse ADT. Where I have integrated Google Map Utility together with Google Play services.

I have made to java binding projects in Mono VS environment. One for Google-Play-services and One for Google Map Utility.

I can see that several classes are never bound even I have added working Jars. (Inspected with unZip/jar)

I get a huge number of binding errors


JARTOXML : warning J2X9001: Couldn't load class com/google/maps/android/MarkerManager : java.lang.NoClassDefFoundError: com/google/android/gms/maps/GoogleMap$OnInfoWindowClickListener
2>  java.lang.NoClassDefFoundError: com/google/android/gms/maps/GoogleMap$OnCameraChangeListener
2>  	at java.lang.ClassLoader.defineClass1(Native Method)
2>  	at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
2>  	at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
2>  	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
2>  	at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
2>  	at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
2>  	at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
2>  	at java.security.AccessController.doPrivileged(Native Method)
2>  	at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
2>  	at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
2>  	at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
2>  	at jar2xml.JavaArchive.getPackages(JavaArchive.java:86)
2>  	at jar2xml.JavaArchive.getPackages(JavaArchive.java:64)
2>  	at jar2xml.Start.main(Start.java:126)
2>  Caused by: java.lang.ClassNotFoundException: com.google.android.gms.maps.GoogleMap$OnCameraChangeListener
2>  	at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
2>  	at java.security.AccessController.doPrivileged(Native Method)
2>  	at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
2>  	at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
2>  	at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
2>  	... 14 more
2>JARTOXML : warning J2X9001: Couldn't load class com/google/maps/android/clustering/ClusterManager : java.lang.NoClassDefFoundError: com/google/android/gms/maps/GoogleMap$OnCameraChangeListener
2>  java.lang.NoClassDefFoundError: com/google/android/gms/maps/GoogleMap$OnMarkerClickListener


JARTOXML : warning J2X9001: Couldn't load class com/google/maps/android/heatmaps/HeatmapTileProvider : java.lang.NoClassDefFoundError: com/google/android/gms/maps/model/TileProvider
2>JARTOXML : warning J2XA006: missing class error was raised while reflecting com.google.maps.android.MarkerManager$Collection : com/google/android/gms/maps/GoogleMap$OnInfoWindowClickListener
2>JARTOXML : warning J2XA006: missing class error was raised while reflecting com.google.maps.android.PolyUtil : com/google/android/gms/maps/model/LatLng
2>JARTOXML : warning J2XA006: missing class error was raised while reflecting com.google.maps.android.SphericalUtil : com/google/android/gms/maps/model/LatLng
2>JARTOXML : warning J2XA006: missing class error was raised while reflecting com.google.maps.android.clustering.Cluster : com/google/android/gms/maps/model/LatLng
2>JARTOXML : warning J2XA006: missing class error was raised while reflecting com.google.maps.android.clustering.ClusterItem : com/google/android/gms/maps/model/LatLng
2>JARTOXML : warning J2XA006: missing class error was raised while reflecting com.google.maps.android.clustering.ClusterManager$ClusterTask : com/google/android/gms/maps/GoogleMap$OnCameraChangeListener
2>JARTOXML : warning J2XA006: missing class error was raised while reflecting com.google.maps.android.clustering.algo.NonHierarchicalDistanceBasedAlgorithm$QuadItem : com/google/android/gms/maps/model/LatLng
2>JARTOXML : warning J2XA006: missing class error was raised while reflecting com.google.maps.android.clustering.algo.StaticCluster : com/google/android/gms/maps/model/LatLng
2>JARTOXML : warning J2XA006: missing class error was raised while reflecting com.google.maps.android.clustering.view.ClusterRenderer : com/google/android/gms/maps/GoogleMap$OnCameraChangeListener
2>JARTOXML : warning J2XA006: missing class error was raised while reflecting com.google.maps.android.clustering.view.DefaultClusterRenderer : com/google/android/gms/maps/GoogleMap$OnMarkerClickListener
2>JARTOXML : warning J2XA006: missing class error was raised while reflecting com.google.maps.android.clustering.view.DefaultClusterRenderer$CreateMarkerTask : com/google/android/gms/maps/model/LatLng
2>JARTOXML : warning J2XA006: missing class error was raised while reflecting com.google.maps.android.clustering.view.DefaultClusterRenderer$MarkerCache : com/google/android/gms/maps/model/Marker
2>JARTOXML : warning J2XA006: missing class error was raised while reflecting com.google.maps.android.clustering.view.DefaultClusterRenderer$MarkerModifier : com/google/android/gms/maps/model/LatLng
2>JARTOXML : warning J2XA006: missing class error was raised while reflecting com.google.maps.android.clustering.view.DefaultClusterRenderer$MarkerWithPosition : com/google/android/gms/maps/model/Marker
2>JARTOXML : warning J2XA006: missing class error was raised while reflecting com.google.maps.android.clustering.view.DefaultClusterRenderer$RenderTask : com/google/android/gms/maps/Projection
2>JARTOXML : warning J2XA006: missing class error was raised while reflecting com.google.maps.android.clustering.view.DefaultClusterRenderer$ViewModifier : com/google/android/gms/maps/GoogleMap
2>JARTOXML : warning J2XA006: missing class error was raised while reflecting com.google.maps.android.heatmaps.HeatmapTileProvider$Builder : com/google/android/gms/maps/model/TileProvider
2>JARTOXML : warning J2XA006: missing class error was raised while reflecting com.google.maps.android.heatmaps.WeightedLatLng : com/google/android/gms/maps/model/LatLng
2>JARTOXML : warning J2XA006: missing class error was raised while reflecting com.google.maps.android.projection.SphericalMercatorProjection : com/google/android/gms/maps/model/LatLng
2>    RemovedDirectories: 

I have tried with manual JNI binding and Metadata file but without success.

See attachment. Eclipse and VS screen dump and binding log
Comment 1 ole.kristensen 2014-03-04 13:50:14 UTC
Created attachment 6215 [details]
New binding log. Without dll reference to google play service dll
Comment 2 Atsushi Eno 2014-03-07 05:40:09 UTC
You either have to add reference to Google Play Services binding dll (component, or https://github.com/xamarin/monodroid-samples/tree/master/GooglePlayServices ) in your binding project, or add google play services library (build within Java land first) as LibraryProjectZip.

If that does not resolve the problem (a binding for library.jar successfully built for me), please attach your project so that we can find out what goes wrong.
Comment 3 ole.kristensen 2014-03-07 07:58:57 UTC
Currently I'm using 2 binding projects. Maybe that the problem

GoogleMapV2(google_play_services) and GoogleMapsUtility

https://github.com/OleFalkerslevKristensen/GoogleMapUtility2
Comment 4 ole.kristensen 2014-03-10 06:16:36 UTC
Could You please take a look at my project, what I'm doing wrong
Comment 5 Jonathan Pryor 2014-03-12 13:23:50 UTC
@ole.kristensen: Consider your GoogleMapsUtility project. It currently warns with:

>                 Tool /usr/bin/java execution started with arguments: -jar /Library/Frameworks/Xamarin.Android.framework/Versions/Current/lib/mandroid/jar2xml.jar --jar=/Users/jon/Downloads/GoogleMapUtility2/MapsAn
> dLocationDemo_v2/GoogleMapsUtility/Jars/library.jar --ref=/opt/android/sdk/platforms/android-8/android.jar --1out=/Users/jon/Downloads/GoogleMapUtility2/MapsAndLocationDemo_v2/GoogleMapsUtility/obj/Debug/api.xml 
>                 java.lang.NoClassDefFoundError: com/google/android/gms/maps/GoogleMap$OnInfoWindowClickListener
>                         at java.lang.ClassLoader.defineClass1(Native Method)
>                         at java.lang.ClassLoader.defineClassCond(ClassLoader.java:637)
>                         at java.lang.ClassLoader.defineClass(ClassLoader.java:621)
>                         at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
>                         at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
>                         at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
>                         at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
>                         at java.security.AccessController.doPrivileged(Native Method)
>                         at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
>                         at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
>                         at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
>                         at jar2xml.JavaArchive.getPackages(JavaArchive.java:86)
>                         at jar2xml.JavaArchive.getPackages(JavaArchive.java:64)
>                         at jar2xml.Start.main(Start.java:126)
>                 Caused by: java.lang.ClassNotFoundException: com.google.android.gms.maps.GoogleMap$OnInfoWindowClickListener
>                         at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
>                         at java.security.AccessController.doPrivileged(Native Method)
>                         at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
>                         at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
>                         at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
>                         ... 14 more

The warning is generated because com.google.android.gms.maps.GoogleMap$OnInfoWindowClickListener can't be found. Where is it? It's not defined in either of the .jar files that jar2xml.jar is referencing (library.jar and android.jar)

If I search your git repo, I do find com.google.android.gms.maps.GoogleMap$OnInfoWindowClickListener in GoogleMapV2/Jars/google-play-services.jar:

$ jar tf GoogleMapV2/Jars/google-play-services.jar | grep OnInfoWin
com/google/android/gms/maps/GoogleMap$OnInfoWindowClickListener.class

Consequently, if we edit GoogleMapUtility2/MapsAndLocationDemo_v2/GoogleMapsUtility/GoogleMapsUtility.csproj and add a $(ReferenceJar) to google-play-services.jar:

    <ReferenceJar Include="..\GoogleMapV2\Jars\google-play-services.jar" />

This results in google-play-services.jar being referenced:

>                Tool /usr/bin/java execution started with arguments: -jar /Library/Frameworks/Xamarin.Android.framework/Versions/Current/lib/mandroid/jar2xml.jar --jar=/Users/jon/Downloads/GoogleMapUtility2/MapsAndLocationDemo_v2/GoogleMapsUtility/Jars/library.jar --ref=/opt/android/sdk/platforms/android-8/android.jar --out=/Users/jon/Downloads/GoogleMapUtility2/MapsAndLocationDemo_v2/GoogleMapsUtility/obj/Debug/api.xml --ref=/Users/jon/Downloads/GoogleMapUtility2/MapsAndLocationDemo_v2/GoogleMapV2/Jars/google-play-services.jar 

And no more warnings for GoogleMap$OnInfoWindowClickListener are generated. (Instead, I get a warning about android/animation/ValueAnimator$AnimatorUpdateListener not being found, but that's because the $(TargetFrameworkVersion) is v2.2 (API-8), while AnimationUpdateListener was added in v3.0 (API-11).

Alternatively, instead of adding a $(ReferenceJar) to google-play-services.jar, you can add an Assembly reference to an assembly that embeds google-play-services.jar. (Unfortunately the googleplayservicesfroyo component doesn't embed google-play-services.jar, due to licensing issues.)

Does this clarify anything? Any specific questions?
Comment 6 ole.kristensen 2014-03-12 15:22:56 UTC
Thanks. Only a few critical warning remains now

 JARTOXML : warning J2XA001: missing class error was raised while reflecting com.google.maps.android.clustering.view.DefaultClusterRenderer$CreateMarkerTask [public com.google.maps.android.clustering.view.DefaultClusterRenderer$CreateMarkerTask(com.google.maps.android.clustering.view.DefaultClusterRenderer,com.google.maps.android.clustering.Cluster,java.util.Set,com.google.android.gms.maps.model.LatLng)] : com/google/maps/android/R
2>JARTOXML : warning J2XA006: missing class error was raised while reflecting com.google.maps.android.clustering.view.DefaultClusterRenderer$ViewModifier : com/google/maps/android/R

I guess that's why the DefaultClusterRenderer is not included in the final DLL.

What could be the reason here?
Comment 7 Jonathan Pryor 2014-03-13 10:38:17 UTC
> : com/google/maps/android/R

This is an unfortunate interaction between "By Android Design" and a current tooling limitations.

.jar files aren't supposed to contain the R type.

Android resource values are "funny". Ostensibly they're `final int`s defined by the application project. However, since they're defined by the application project, Android Library projects couldn't use resources. This was "solved" by changing things up so that an Android Library project would have non-`final` int fields, which the library could reference, and then the Application project would continue to have `final` int fields and would update the Library field values so that they were correct at runtime.

Which is why .jar files aren't supposed to contain the R type; the Application project is supposed to re-generate them as part of the build process so it will be properly in-sync, and so that App code can update the values at runtime (and know which types to update).

Thus, com.google.maps.android.R is _supposed_ to be missing. That's By Android Design.

Then we hit our unfortunate tooling limitation: our .jar parser can (will) misbehave if types can't be found:

https://bugzilla.xamarin.com/show_bug.cgi?id=15885#c11

> I guess that's why the DefaultClusterRenderer is not included in the final DLL.

Correct.

There are two workarounds:

1. Manually use JNIEnv to bind the type + methods.
2. Manually write the binding XML required.

Both involve more work than is ideal. (1) is good if you don't need to override any virtual methods/implement any interfaces and you only need to invoke a few members, otherwise I'd advocate (2), which you can do by editing Transforms\Metadata.xml and using <add-node/>, e.g.

  <add-node path="/api/package[@name='com.google.maps.android.clustering.view']">
    <class abstract="false" final="false" name="DefaultClusterRenderer" static="false" visibility="public">
      <!-- ... -->
    </class>
  </add-node>

(Basically just copy obj/Debug/api.xml for the "basic idea" and adjust for DefaultClusterRenderer. Familiarity with `javap` would be useful as well.)