Binding a jar referencing a shared objects c-library (.so)

StefanDipplStefanDippl USMember ✭✭
edited January 2016 in Xamarin.Android

Hi!

This is actually a question of moving away from Xamarin and back to native Android development, which would be utterly sad, because we sank so much time in our app so far.

I fail in binding java libs which are referencing .so files for our project.

We're developing an app with a VoIP call feature. The only ready-to-use Xamarin component for this is the Twilio Client, which is currently built into our app. But it's poorly maintained and never up to date. So I tried to bind the Twilio Client from the latest official Twilio Client Android SDK release myself and ended up with a lot of build errors.

I followed the instructions of the Binding a Java Library guide

Unfortunately the Troubleshooting Binding Guide wasn't helping much and I think I need the advice of an experienced programmer or someone from the Xamarin Support to help me find the correct approach how to fix this.

At the moment I'm trying to bind another VoIP client SDK from Sinch, which didn't work either.

This is how the library is set up in my project
NativeSinchClient (BindingsLibrary) ├┬ Jars │├┬ armeabi ││└ libsinch-android-rtc.so (EmbeddedNativeLibrary, do not copy) │├┬ armeabi-v7a ││└ libsinch-android-rtc.so (EmbeddedNativeLibrary, do not copy) │└┬ x84 │ └ libsinch-android-rtc.so (EmbeddedNativeLibrary, do not copy) └ sinch-android-rtc-3.7.1.jar (EmbeddedJar, do not copy)

The Sinch project is attached to this question.

Any help highly appreciated. Thanks,
Stefan

P.S. This StackOverflow question deals with the same issue.

Tagged:

Best Answer

Answers

  • DirkDenzerDirkDenzer USMember ✭✭
    edited January 2016

    I was able to build a library with .so files by adding the jar into the jars folder and set the build action to EmbeddedJar.
    I added the .so files to a new folder libs (which was a direct subfolder of BindingProject (the same level as the jars folder)) and added the folder structure you mentioned to it and copied all needed .so files into them.

    The .so files Build Aciton is set to EmbeddedNativeLibrary.
    This would be the folder structure in a native Android project. Maybe it helps.

    P.S. if you end up with a lot of error messages, could you provide them?

    BR

  • StefanDipplStefanDippl USMember ✭✭

    Hi Dirk!

    Thanks for the input. Unfortunately changing the folder structure results in the exact same outcome / error messages.

    Did the project in your case built right away without any changes to the Metadata.xml?

    Best,
    Stefan

  • DirkDenzerDirkDenzer USMember ✭✭

    Yes, there weren't any error messages and noch changes at the metadata were necessary.
    What errors do you get?

  • StefanDipplStefanDippl USMember ✭✭

    Amongst 44 warnings I get 14 errors trying to bin the Sinch library:

    obj/Debug/generated/src/Com.Sinch.Android.Rtc.Internal.Client.ConfigRefresher.cs(9,87): error CS0234: The type or namespace name 'UserAgentMethodInvocationScheduler' does not exist in the namespace 'Com.Sinch.Android.Rtc.Internal.Client'. Are you missing an assembly reference? obj/Debug/generated/src/Com.Sinch.Android.Rtc.Internal.Client.HouseKeeper.cs(9,83): error CS0234: The type or namespace name 'UserAgentMethodInvocationScheduler' does not exist in the namespace 'Com.Sinch.Android.Rtc.Internal.Client'. Are you missing an assembly reference? obj/Debug/generated/src/Com.Rebtel.Repackaged.Com.Google.Gson.Internal.Bind.CollectionTypeAdapterFactory.cs(70,57): error CS0533: 'Com.Rebtel.Repackaged.Com.Google.Gson.Internal.Bind.CollectionTypeAdapterFactory.Adapter.Read(Com.Rebtel.Repackaged.Com.Google.Gson.Stream.JsonReader)' hides inherited abstract member 'Com.Rebtel.Repackaged.Com.Google.Gson.TypeAdapter.Read(Com.Rebtel.Repackaged.Com.Google.Gson.Stream.JsonReader)' obj/Debug/generated/src/Com.Rebtel.Repackaged.Com.Google.Gson.Internal.Bind.DateTypeAdapter.cs(72,40): error CS0533: 'Com.Rebtel.Repackaged.Com.Google.Gson.Internal.Bind.DateTypeAdapter.Read(Com.Rebtel.Repackaged.Com.Google.Gson.Stream.JsonReader)' hides inherited abstract member 'Com.Rebtel.Repackaged.Com.Google.Gson.TypeAdapter.Read(Com.Rebtel.Repackaged.Com.Google.Gson.Stream.JsonReader)' obj/Debug/generated/src/Com.Rebtel.Repackaged.Com.Google.Gson.Internal.Bind.MapTypeAdapterFactory.cs(72,57): error CS0533: 'Com.Rebtel.Repackaged.Com.Google.Gson.Internal.Bind.MapTypeAdapterFactory.Adapter.Read(Com.Rebtel.Repackaged.Com.Google.Gson.Stream.JsonReader)' hides inherited abstract member 'Com.Rebtel.Repackaged.Com.Google.Gson.TypeAdapter.Read(Com.Rebtel.Repackaged.Com.Google.Gson.Stream.JsonReader)' obj/Debug/generated/src/Com.Rebtel.Repackaged.Com.Google.Gson.Internal.Bind.SqlDateTypeAdapter.cs(72,39): error CS0533: 'Com.Rebtel.Repackaged.Com.Google.Gson.Internal.Bind.SqlDateTypeAdapter.Read(Com.Rebtel.Repackaged.Com.Google.Gson.Stream.JsonReader)' hides inherited abstract member 'Com.Rebtel.Repackaged.Com.Google.Gson.TypeAdapter.Read(Com.Rebtel.Repackaged.Com.Google.Gson.Stream.JsonReader)' obj/Debug/generated/src/Com.Rebtel.Repackaged.Com.Google.Gson.Internal.Bind.TimeTypeAdapter.cs(72,39): error CS0533: 'Com.Rebtel.Repackaged.Com.Google.Gson.Internal.Bind.TimeTypeAdapter.Read(Com.Rebtel.Repackaged.Com.Google.Gson.Stream.JsonReader)' hides inherited abstract member 'Com.Rebtel.Repackaged.Com.Google.Gson.TypeAdapter.Read(Com.Rebtel.Repackaged.Com.Google.Gson.Stream.JsonReader)' obj/Debug/generated/src/Com.Rebtel.Repackaged.Com.Google.Gson.Internal.Bind.TypeAdapters.cs(659,32): error CS0111: A member 'Com.Rebtel.Repackaged.Com.Google.Gson.Internal.Bind.TypeAdapters.EnumTypeAdapter.Write(Com.Rebtel.Repackaged.Com.Google.Gson.Stream.JsonWriter, Java.Lang.Object)' is already defined. Rename this member or use different parameter types obj/Debug/generated/src/Com.Sinch.Android.Rtc.Internal.Client.ConfigRefresher.cs(18,29): error CS0115: 'Com.Sinch.Android.Rtc.Internal.Client.ConfigRefresher.ThresholdClass' is marked as an override but no suitable property found to override obj/Debug/generated/src/Com.Sinch.Android.Rtc.Internal.Client.ConfigRefresher.cs(22,42): error CS0115: 'Com.Sinch.Android.Rtc.Internal.Client.ConfigRefresher.ThresholdType' is marked as an override but no suitable property found to override obj/Debug/generated/src/Com.Sinch.Android.Rtc.Internal.Client.ConfigRefresher.cs(79,34): error CS0115: 'Com.Sinch.Android.Rtc.Internal.Client.ConfigRefresher.OnRun(Com.Sinch.Android.Rtc.Internal.Natives.Jni.IUserAgent)' is marked as an override but no suitable method found to override obj/Debug/generated/src/Com.Sinch.Android.Rtc.Internal.Client.HouseKeeper.cs(18,29): error CS0115: 'Com.Sinch.Android.Rtc.Internal.Client.HouseKeeper.ThresholdClass' is marked as an override but no suitable property found to override obj/Debug/generated/src/Com.Sinch.Android.Rtc.Internal.Client.HouseKeeper.cs(22,42): error CS0115: 'Com.Sinch.Android.Rtc.Internal.Client.HouseKeeper.ThresholdType' is marked as an override but no suitable property found to override obj/Debug/generated/src/Com.Sinch.Android.Rtc.Internal.Client.HouseKeeper.cs(79,34): error CS0115: 'Com.Sinch.Android.Rtc.Internal.Client.HouseKeeper.OnRun(Com.Sinch.Android.Rtc.Internal.Natives.Jni.IUserAgent)' is marked as an override but no suitable method found to override

  • StefanDipplStefanDippl USMember ✭✭

    Hey Dirk!

    Thanks for taking the time to dig into this and sharing it here. I really appreciate it. I'll try and keep you updated how far I get.

    Best,
    Stefan

  • StefanDipplStefanDippl USMember ✭✭

    Thanks Dirk with your help I made it work!

    A few learnings that might help others struggling with binding a library

    You can't hurt the library

    The most important information for anyone new to binding libs is that you can remove every binding you don't actually need for your own code to work, as the library itself (.jar, .so) won't be affected by it.

    No need to guess node paths

    Another important note is: You don't have to figure out the path to a node yourself. Just go to the location where the error/warnig occurs and it will be there in a comment.

    Example warning:

    obj/Release/generated/src/Com.Sinch.Android.Rtc.Internal.Client.Scheduler.cs(141,30): warning CS0108: 'Com.Sinch.Android.Rtc.Internal.Client.Scheduler.Dispose()' hides inherited member 'Java.Lang.Object.Dispose()'. Use the new keyword if hiding was intended
    

    The corresponding code will have the path in its comments:
    // Metadata.xml XPath method reference: path="/api/package[@name='com.sinch.android.rtc.internal.client']/class[@name='Scheduler']/method[@name='dispose' and count(parameter)=0]" [Register ("dispose", "()V", "GetDisposeHandler")] public virtual unsafe void Dispose ()

    These are the modification I had to make for the Sinch library (v3.7.1) to work with Xamarin

    in file: Transform/Metadata.xml

    <metadata>
        <!-- 
        Removes 6 errors:
        obj/Debug/generated/src/Com.Rebtel.Repackaged.Com.Google.Gson.Internal.Bind.CollectionTypeAdapterFactory.cs(70,57): error CS0533: 'Com.Rebtel.Repackaged.Com.Google.Gson.Internal.Bind.CollectionTypeAdapterFactory.Adapter.Read(Com.Rebtel.Repackaged.Com.Google.Gson.Stream.JsonReader)' hides inherited abstract member 'Com.Rebtel.Repackaged.Com.Google.Gson.TypeAdapter.Read(Com.Rebtel.Repackaged.Com.Google.Gson.Stream.JsonReader)'
        obj/Debug/generated/src/Com.Rebtel.Repackaged.Com.Google.Gson.Internal.Bind.DateTypeAdapter.cs(72,40): error CS0533: 'Com.Rebtel.Repackaged.Com.Google.Gson.Internal.Bind.DateTypeAdapter.Read(Com.Rebtel.Repackaged.Com.Google.Gson.Stream.JsonReader)' hides inherited abstract member 'Com.Rebtel.Repackaged.Com.Google.Gson.TypeAdapter.Read(Com.Rebtel.Repackaged.Com.Google.Gson.Stream.JsonReader)'
        obj/Debug/generated/src/Com.Rebtel.Repackaged.Com.Google.Gson.Internal.Bind.MapTypeAdapterFactory.cs(72,57): error CS0533: 'Com.Rebtel.Repackaged.Com.Google.Gson.Internal.Bind.MapTypeAdapterFactory.Adapter.Read(Com.Rebtel.Repackaged.Com.Google.Gson.Stream.JsonReader)' hides inherited abstract member 'Com.Rebtel.Repackaged.Com.Google.Gson.TypeAdapter.Read(Com.Rebtel.Repackaged.Com.Google.Gson.Stream.JsonReader)'
        obj/Debug/generated/src/Com.Rebtel.Repackaged.Com.Google.Gson.Internal.Bind.SqlDateTypeAdapter.cs(72,39): error CS0533: 'Com.Rebtel.Repackaged.Com.Google.Gson.Internal.Bind.SqlDateTypeAdapter.Read(Com.Rebtel.Repackaged.Com.Google.Gson.Stream.JsonReader)' hides inherited abstract member 'Com.Rebtel.Repackaged.Com.Google.Gson.TypeAdapter.Read(Com.Rebtel.Repackaged.Com.Google.Gson.Stream.JsonReader)'
        obj/Debug/generated/src/Com.Rebtel.Repackaged.Com.Google.Gson.Internal.Bind.TimeTypeAdapter.cs(72,39): error CS0533: 'Com.Rebtel.Repackaged.Com.Google.Gson.Internal.Bind.TimeTypeAdapter.Read(Com.Rebtel.Repackaged.Com.Google.Gson.Stream.JsonReader)' hides inherited abstract member 'Com.Rebtel.Repackaged.Com.Google.Gson.TypeAdapter.Read(Com.Rebtel.Repackaged.Com.Google.Gson.Stream.JsonReader)'
        obj/Debug/generated/src/Com.Rebtel.Repackaged.Com.Google.Gson.Internal.Bind.TypeAdapters.cs(659,32): error CS0111: A member 'Com.Rebtel.Repackaged.Com.Google.Gson.Internal.Bind.TypeAdapters.EnumTypeAdapter.Write(Com.Rebtel.Repackaged.Com.Google.Gson.Stream.JsonWriter, Java.Lang.Object)' is already defined. Rename this member or use different parameter types
        -->
        <remove-node path="/api/package[starts-with(@name, 'com.rebtel.repackaged.com.google.gson')]" />
    
        <!-- 
        Removes 8 errors:
        obj/Debug/generated/src/Com.Sinch.Android.Rtc.Internal.Client.ConfigRefresher.cs(9,87): error CS0234: The type or namespace name 'UserAgentMethodInvocationScheduler' does not exist in the namespace 'Com.Sinch.Android.Rtc.Internal.Client'. Are you missing an assembly reference?
        obj/Debug/generated/src/Com.Sinch.Android.Rtc.Internal.Client.HouseKeeper.cs(9,83): error CS0234: The type or namespace name 'UserAgentMethodInvocationScheduler' does not exist in the namespace 'Com.Sinch.Android.Rtc.Internal.Client'. Are you missing an assembly reference?
        obj/Debug/generated/src/Com.Sinch.Android.Rtc.Internal.Client.ConfigRefresher.cs(18,29): error CS0115: 'Com.Sinch.Android.Rtc.Internal.Client.ConfigRefresher.ThresholdClass' is marked as an override but no suitable property found to override
        obj/Debug/generated/src/Com.Sinch.Android.Rtc.Internal.Client.ConfigRefresher.cs(22,42): error CS0115: 'Com.Sinch.Android.Rtc.Internal.Client.ConfigRefresher.ThresholdType' is marked as an override but no suitable property found to override
        obj/Debug/generated/src/Com.Sinch.Android.Rtc.Internal.Client.ConfigRefresher.cs(79,34): error CS0115: 'Com.Sinch.Android.Rtc.Internal.Client.ConfigRefresher.OnRun(Com.Sinch.Android.Rtc.Internal.Natives.Jni.IUserAgent)' is marked as an override but no suitable method found to override
        obj/Debug/generated/src/Com.Sinch.Android.Rtc.Internal.Client.HouseKeeper.cs(18,29): error CS0115: 'Com.Sinch.Android.Rtc.Internal.Client.HouseKeeper.ThresholdClass' is marked as an override but no suitable property found to override
        obj/Debug/generated/src/Com.Sinch.Android.Rtc.Internal.Client.HouseKeeper.cs(22,42): error CS0115: 'Com.Sinch.Android.Rtc.Internal.Client.HouseKeeper.ThresholdType' is marked as an override but no suitable property found to override
        obj/Debug/generated/src/Com.Sinch.Android.Rtc.Internal.Client.HouseKeeper.cs(79,34): error CS0115: 'Com.Sinch.Android.Rtc.Internal.Client.HouseKeeper.OnRun(Com.Sinch.Android.Rtc.Internal.Natives.Jni.IUserAgent)' is marked as an override but no suitable method found to override
        -->
        <attr path="/api/package[@name='com.sinch.android.rtc.internal.client']/class[@name='UserAgentMethodInvocationScheduler']" name="visibility">public</attr>
    
        <!-- Removes 10 warnings --> 
        <remove-node path="/api/package[starts-with(@name, 'com.sinch.android.rtc.internal.service.pubnub')]" />
    </metadata>
    

    Sometimes a method is missing. E.g. the error I got was this:

    obj/Release/generated/src/Com.Sinch.Android.Rtc.Internal.Client.Calling.DefaultCallClient.cs(9,23): error CS0535: 'Com.Sinch.Android.Rtc.Internal.Client.Calling.DefaultCallClient' does not implement interface member 'Com.Sinch.Android.Rtc.Calling.ICallClient.SetRespectNativeCalls(bool)'
    

    In my case it was enough to add a partial class with the right namespace and class name to the Additions/Fixes.cs file (you can have multiple partial classes in this file if needed) and add a dummy method with the right parameters.

    in file: Addition/Fixes.cs
    namespace Com.Sinch.Android.Rtc.Internal.Client.Calling { partial class DefaultCallClient { public void SetRespectNativeCalls(bool flag) {} } }

    Hope this helps others.

    Best,
    Stefan

Sign In or Register to comment.