我正在尝试从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?简单地创建具有相同功能的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中得到通知?
答案 0 :(得分:4)
您可以通过参考在VS201X中将这些项目链接在一起。从这里开始,您应该能够填写其他层(JNI / Java),然后开始在系统周围传递指针(作为一个长整数)并调用您的函数。
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.
}
}
}
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 <string>
#include <msclr/marshal_cppstd.h>
#include <msclr/marshal.h>
using namespace msclr::interop;
using namespace CLILayer;
CLIObject::CLIObject()
{
}
CLIObject::~CLIObject()
{
}
CLIObject::loadJava(System::String^ jvmLocaion, System::String^ classpath)
{
std::string _jvmLoc = marshal_as<std::string>(jvmLocation);
std::string _classpath = marshal_as<std::string>(classpath);
}
答案 1 :(得分:3)
这是 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<string, string> jvmParameters = new Dictionary<string, string>();
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方法的所有参数。由于它是一个对象列表,它可以保存不同类型的参数,因为所有内容都是从对象继承的。
List<object> olParameters = new List<object>();
olParameters.Add(Value of the parameter to be passed to the java method);
接下来,只需调用泛型方法CallMethod,将Java方法的返回类型作为模板类型传递。在下面的例子中,我调用了CallMethod,这意味着我想调用的Java方法的返回类型是一个字符串。
接下来,传入要调用的Java方法的名称和方法描述符(参见上文);最后,传递所有参数的列表。 (注意:如果不需要参数,则传入一个空列表。)
Java.CallMethod<string>("AddTwoNumbers", "(IILjava/lang/String;)I", olParameters);
嗯,我想这包装了一切,但请记住,你可以用JNI做更多的事情。测试应用程序只是一种快速而肮脏的方式来演示JNI组件的基础知识。为了全面了解JNI。
您可以在link
上获得更多信息答案 2 :(得分:3)
如果您在Unity3D的上下文中,从Java本机代码到Unity3D C#脚本进行交互的更好方法是调用UnitySendMessage
方法。您可以在Java代码中调用此方法,并将消息发送到C#,这样您就可以获得在C#中执行的指定方法。
您可以在Unity场景中添加gameObject并创建一个包含这三种方法的MonoBehavior脚本(paySucess(字符串消息),payFailed(字符串消息)和payCancel(消息))。然后将新创建的脚本附加到gameObject(让我们假设此gameObject的名称为“PayListener”)并确保在执行Java代码时场景中存在gameObject(您可以在其上调用DontDestroyOnLoad
例如,Awake
中的gameObject。
然后,在您的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文件并将其加载为AndroidJavaClass
或AndroidJavaObject
,当调用Java中的那个时,应调用PayListener脚本中的相应方法。
有关使用UnitySendMessage的Android插件的文档,您可以访问示例3中有关如何实现Android JNI插件here的官方指南。
答案 3 :(得分:1)
看看http://jni4net.sourceforge.net/。我已成功使用它在CLR和JVM之间进行通信。可以找到调用.NET类的Java应用程序示例here。也支持事件(绑定到java侦听器接口)。
答案 4 :(得分:1)
我自己最终解决了这个问题,其他答案在这里发布了,但很遗憾没有解决我遇到的问题是在Unity中。
我解决它的方法是在Java中使用&#39; GetResult&#39;编写自定义侦听器类。函数返回一个字符串,解释结果是什么(即调用了哪个函数,结果是什么),以及C#通过AndroidJNI调用的结果的get函数。
在进行购买之后,我需要从C#调用get函数,直到我得到结果。
答案 5 :(得分:0)
听起来像一团糟。你想要完成什么,为什么?为什么不单独运行JVM和CLR,通过REST调用或类似的东西进行接口?