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
