Maik,

You've probably considered this already, but it might be worth going down a
non-JNI route if possible, until the JNI-friendly Mono for Android changes
come out...

I've had a couple of cases (printing and sat nav) which would suit JNI but
which I actually implemented using an intermediate Java application. So the
main app would launch this intermediate app using an intent/StartActivity,
supplying any necessary information (as, say, an XML string) via PutExtra.
The intermediate app would then communicate with the third party in Java and
return any result at the end. This ends up being similar to using the camera
or selecting images from the Gallery, the Android intents paradigm lends
itself to this quite nicely I think.

This has worked perfectly well in my cases. Obviously it will depend on what
you actually need to do, but it's definitely worth considering.

Cheers,

Andy

-----Original Message-----
From: [email protected]
[mailto:[email protected]] On Behalf Of Jonathan Pryor
Sent: 20 March 2012 15:07
To: Discussions related to Mono for Android
Subject: Re: [mono-android] JniEnv problem

On Mar 20, 2012, at 10:10 AM, Hänke, Maik wrote:

I suspect that the problem is that your native method wants `int
lIdentifier`:

> public static native int Msg_UpdateOptions(int lIdentifier, boolean
bEnabled, boolean bQueue, String functionName, int convention, Object
callBackObj);
>  
> I import the method with this code:
> public static int Msg_UpdateOptions(uint lIdentifier, bool bEnabled, bool
bQueue, String functionName, int convention, Java.Lang.Object callBackObj)

Yet you declared the parameter as `uint`. Java doesn't support uint, so when
you construct a JValue around it:

>              new JValue(lIdentifier),

it will probably be invoking the JValue(long) constructor, not the
JValue(int) constructor. The result, presumably, is that you're passing a
long when an int is expected, and screwing up the stack.

I would suggest that you instead do:

        using (var _functionName = new Java.Lang.String(functionName))
                return JNIEnv.CallStaticIntMethod(mAlkClass, methodId, new
JValue[] {
                                new JValue((int) lIdentifier),  // cast uint
to int, so we don't implicitly convert uint to long
                                new JValue(bEnabled),
                                new JValue(bQueue),
                                new JValue(_functionName),
                                new JValue(convention),
                                new JValue(callBackObj)
                });

The `using` is so that we don't retain a gref for the string longer than
necessary.

> In the Java sample the method is used in this way:
> Msg_UpdateOptions(AlkMsg.MSG_ID_TurnInstructions, true, false,
"OnTurnInstructionReceived", 0, this );
>  
> Where ‚this‘ is an activity and OnTurnInstructionReceived is a method of
the actvity.

This will be problematic, as what your Java library will have access to is
the generated Android Callable Wrapper, which will not have a declaration
for OnTurnInstructionReceived(). The result is that the method lookup will
fail, presumably resulting in an exception at runtime.

The forthcoming (alpha!) 4.1 release will provide an [Export] custom
attribute to support this scenario. In the meantime, you're probably looking
at a painful world of declaring a Java-side interface and hand-writing a C#
binding to that Java interface. For an example, see:

        
https://github.com/xamarin/monodroid-samples/blob/master/SanityTests/Adder.j
ava
        
https://github.com/xamarin/monodroid-samples/blob/master/SanityTests/Managed
Adder.cs

For example, you'd need to add a TurnInstructionReceived.java file to your
project, setting its build action to AndroidJavaSource:

        package demo;

        public interface TurnInstructionReceived {
                void OnTurnInstructionReceived(int buffer, int len);
        }

Then add a "fleshed out" version of a C# binding for the above Java
interface:

        // see
https://github.com/xamarin/monodroid-samples/blob/master/SanityTests/Managed
Adder.cs#L82
        [Register("demo/TurnInstructionReceived", DoNotGenerateAcw=true)]
        interface ITurnInstructionReceived : Android.Runtime.IJavaObject {
                // Note: TODO_AQN is the AssemblyQualifiedName of the
assembly containing this type.
                [Register ("OnTurnInstructionReceived", "(II)V",
"GetOnTurnInstructionReceivedHandler: ITurnInstructionReceivedInvoker,
TODO_AQN")]
                void OnTurnInstructionReceived(int buffer, int len);
        }

        class ITurnInstructionReceivedInvoker : Java.Lang.Object,
ITurnInstructionReceived {
                // ...lots of stuff omitted...
                static Delegate cb_OnTurnInstructionReceived;
                static Delegate GetOnTurnInstructionReceivedHandler ()
                {
                        if (cb_OnTurnInstructionReceived == null)
                                cb_OnTurnInstructionReceived =
JNINativeWrapper.CreateDelegate((Action<IntPtr, IntPtr, int, int>)
n_OnTurnInstructionReceived);
                        return cb_OnTurnInstructionReceived;
                }

                static void n_OnTurnInstructionReceived(IntPtr jnienv,
IntPtr lrefThis, int buffer, int len)
                {
                        ITurnInstructionReceived __this =
Java.Lang.Object.GetObject<ITurnInstructionReceived >(lrefThis,
JniHandleOwnership.DoNotTransfer);
                        __this.OnTurnInstructionReceived(buffer, len);
                }
        }

With the above two files, you could then modify your activity to implement
ITurnInstructionReceived, allowing Msg_UpdateOptions() to invoke the
OnTurnInstructionReceived() method.

 - Jon

_______________________________________________
Monodroid mailing list
[email protected]

UNSUBSCRIBE INFORMATION:
http://lists.ximian.com/mailman/listinfo/monodroid

_______________________________________________
Monodroid mailing list
[email protected]

UNSUBSCRIBE INFORMATION:
http://lists.ximian.com/mailman/listinfo/monodroid

Reply via email to