[英]JNI4NET - how to run Java application from C# Class Library project?
[英]How to pass a JNI C# class into Java or handle this situation?
我試圖從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中得到通知?
您可以通過參考在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);
}
這是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。
您可以通過此鏈接獲得更多信息
如果你在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文件並將其加載為AndroidJavaClass
或AndroidJavaObject
,當調用Java中的那個時,應調用PayListener腳本中的相應方法。
對於Android插件與UnitySendMessage的文檔,你可以參觀如何實現一個Android的JNI插件的官方指南這里 ,在具體例3。
看看http://jni4net.sourceforge.net/ 。 我已成功使用它在CLR和JVM之間進行通信。 可以在此處找到調用.NET類的Java應用程序示例。 還支持事件(綁定到java偵聽器接口)。
我自己最終解決了這個問題,其他答案在這里發布了,但不幸的是沒有解決我面臨的問題是在Unity中。
我解決它的方法是在Java中使用'GetResult'函數編寫一個自定義偵聽器類,該函數返回一個字符串,解釋結果是什么(即調用了哪個函數以及結果是什么)以及C#調用的結果的get函數通過AndroidJNI。
在進行購買調用之后,需要從C#調用get函數,直到我得到結果。
聽起來像一團糟。 你想要完成什么,為什么? 為什么不單獨運行JVM和單獨運行CLR,通過REST調用或類似的方式進行接口?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.