如何将JNI C#类传递给Java或处理这种情况?

我试图从C#调用Java方法,它从java中调用如下:

EgamePay.pay(thisActivity, payAlias, new EgamePayListener() { @Override public void paySuccess(String alias) { } @Override public void payFailed(String alias, int errorInt) { } @Override public void payCancel(String alias) { } }); 

前两个参数都可以,但是如何在C#中传递EgamePayListner? 简单地创建具有相同function的C#类将无法正常工作……

这是我目前正在做的事情:

 using (AndroidJavaClass jc = new AndroidJavaClass("cn.egame.terminal.smspay.EgamePay")) { System.IntPtr cls_Activity = AndroidJNI.FindClass("com/unity3d/player/UnityPlayer"); System.IntPtr fid_Activity = AndroidJNI.GetStaticFieldID(cls_Activity, "currentActivity", "Landroid/app/Activity;"); object[] p = { fid_Activity, "payAlias", new EgamePayListener() }; jc.CallStatic("pay", p); } .. class EgamePayListener { public void paySucess(string alias) { } public void payFailed(string alians, int errorInt) { } public void payCancel(string alias) { } } 

显然这是错的,我怎么能处理这种情况,以便在这些函数被触发时我可以在C#land中得到通知?

如果您在Unity3D的上下文中,从Java本机代码到Unity3D C#脚本进行交互的更好方法是调用UnitySendMessage方法。 您可以在Java代码中调用此方法,并将消息发送到C#,这样您就可以获得在C#中执行的指定方法。

您可以在Unity场景中添加gameObject并创建一个包含这三种方法的MonoBehavior脚本(paySucess(字符串消息),payFailed(字符串消息)和payCancel(消息))。 然后将新创建的脚本附加到gameObject(让我们假设此gameObject的名称为“PayListener”)并确保执行Java代码时场景中存在gameObject(您可以在Awake调用DontDestroyOnLoad上的DontDestroyOnLoad,例如)。

然后,在您的Java代码中,只需编写如下:

 EgamePay.pay(thisActivity, payAlias, new EgamePayListener() { @Override public void paySuccess(String alias) { com.unity3d.player.UnityPlayer.UnitySendMessage("PayListener","paySuccess", alias); } @Override public void payFailed(String alias, int errorInt) { com.unity3d.player.UnityPlayer.UnitySendMessage("PayListener","payFailed", alias + "#" + errorInt); } @Override public void payCancel(String alias) { com.unity3d.player.UnityPlayer.UnitySendMessage("PayListener","payCancel", alias); } }); 

如果没有com.unity3d.player.UnityPlayer类,它将阻止java文件成功构建。 您可以找到/Applications/Unity/Unity.app/Contents/PlaybackEngines/AndroidPlayer/bin/classes.jar文件,并将其添加为Java项目的依赖库。 然后,您可以无错误地构建,生成和使用新的jar文件。

由于UnitySendMessage方法只能使用1个参数,因此您可能必须通过策略连接payFailed结果别名和errorInt,并在Unity C#侧解析它。

通过使用新构建的jar文件并将其加载为AndroidJavaClassAndroidJavaObject ,当调用Java中的一个时,应调用PayListener脚本中的相应方法。

有关使用UnitySendMessage的Android插件的文档,您可以在示例3中访问有关如何实现Android JNI插件的官方指南。

您可以通过参考在VS201X中将这些项目链接在一起。 从这里开始,您应该能够填写其他层(JNI / Java),然后开始在系统周围传递指针(作为一个长整数)并调用您的函数。

C#层

Program.cs中

 namespace CSharpLayer { class Program : CLILayer.CLIObject { static void Main(string[] args) { Program p = new Program(); p.invokeJava(); } public void invokeJava() { //Call into CLI layer function to loadJVM, call Java code, etc loadJava(); } public override void callback(string data) { //This will be called from the CLI Layer. } } } 

C ++ / CLI层 – 具有CLR支持的DLL C ++项目(/ clr)

CLIObject.h

 #pragma once namespace CLILayer { public ref class CLIObject { public: CLIObject(); ~CLIObject(); void loadJava(System::String^ jvm, System::String^ classpath); virtual void callback(System::String^ data) = 0; }; } 

CLIObject.cpp

 #include "CLIObject.h" #include  #include  #include  using namespace msclr::interop; using namespace CLILayer; CLIObject::CLIObject() { } CLIObject::~CLIObject() { } CLIObject::loadJava(System::String^ jvmLocaion, System::String^ classpath) { std::string _jvmLoc = marshal_as(jvmLocation); std::string _classpath = marshal_as(classpath); } 

这是JNI字段描述符

 JavaLanguage Type -------------------------------- Z boolean B byte C char S short I int J long F float D double Ljava/lang/String; string [Ljava/lang/Object; object[] 

方法描述符使用字段描述符并描述Java方法的结构。 方法描述符中的字段描述符之间没有空格。

除了由V表示的void返回类型之外,所有其他返回类型都使用字段描述符。 下表描述了Java方法声明和相应的JNI描述符。 当通过JNI从C#调用Java方法时,使用JNI方法描述符。

  Java Method Declaration JNI Method Descriptor ---------------------------------------------------- String foo(); "()Ljava/lang/String;" Void bar(int I, bool b); (IZ)V 

首先需要做的是创建一个字典对象,该对象将包含要传递给Java虚拟机的所有参数。 在下面的示例中,我正在设置类路径,它将告诉JVM在哪里查找类和包。

 private Dictionary jvmParameters = new Dictionary(); jvmParameters.Add("-Djava.class.path", Location of the java class); 

将JVM参数分配给字典对象后,即可创建JavaNativeInterface的实例。 创建后,需要使用JVM参数调用LoadJVM方法,然后加载Java虚拟机。 加载后,用户调用该方法来实例化Java对象(请注意,InstantiateJavaObject方法的使用是可选的,因为用户可能只想调用静态方法,在这种情况下,不需要调用此方法;但是,它不会造成任何伤害)。

 Java = new JavaNativeInterface(); Java.LoadVM(jvmParameters, false); Java.InstantiateJavaObject(Name of the java class excluding the extension); 

一旦加载了JVM并实例化了类,用户就可以调用他们想要的任何方法。 首先创建一个对象列表,其中包含要传递给Java方法的所有参数。 由于它是一个对象列表,它可以保存不同类型的参数,因为所有内容都从一个对象inheritance。

  List olParameters = new List(); olParameters.Add(Value of the parameter to be passed to the java method); 

接下来,只需调用generics方法CallMethod,将Java方法的返回类型作为模板类型传递。 在下面的例子中,我调用了CallMethod,这意味着我想调用的Java方法的返回类型是一个字符串。

接下来,传入要调用的Java方法的名称和方法描述符(参见上文); 最后,传递所有参数的列表。 (注意:如果不需要参数,则传入一个空列表。)

 Java.CallMethod("AddTwoNumbers", "(IILjava/lang/String;)I", olParameters); 

好吧,我想这包装了一切,但请记住,你可以用JNI做更多的事情。 测试应用程序只是一种快速而肮脏的方式来演示JNI组件的基础知识。 全面了解JNI。

您可以通过此链接获得更多信息

看看http://jni4net.sourceforge.net/ 。 我已成功使用它在CLR和JVM之间进行通信。 可以在此处找到调用.NET类的Java应用程序示例。 还支持事件(绑定到java侦听器接口)。

我自己最终解决了这个问题,其他答案在这里发布,而不幸的是没有解决我面临的问题是在Unity中。

我解决它的方法是在Java中使用’GetResult’函数编写一个自定义侦听器类,该函数返回一个字符串,解释结果是什么(即调用了哪个函数以及结果是什么)以及C#调用的结果的get函数通过AndroidJNI。

在进行购买调用之后,需要从C#调用get函数,直到我得到结果。

听起来像一团糟。 你想要完成什么,为什么? 为什么不单独运行JVM和单独运行CLR,通过REST调用或类似的方式进行接口?