Как передать класс JNI С# в Java или обработать эту ситуацию?
Я пытаюсь вызвать 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) {
}
});
Первые два параметра в порядке, но как я могу передать EgamePayListner через С#? Простое создание класса С# с теми же функциями не будет работать...
Вот что я делаю в настоящее время:
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)
{
}
}
Очевидно, что это неправильно, как я могу справиться с этой ситуацией, чтобы я мог получать уведомление на земле С#, когда эти функции запущены?
Ответы
Ответ 1
если вы находитесь в контексте Unity3D, лучшим способом взаимодействия с собственным кодом Java с Unity3D С# script будет вызов метода UnitySendMessage
. Вы можете вызвать этот метод в вашем Java-коде, и сообщение будет отправлено на С#, поэтому вы можете получить указанный метод, выполненный на С#.
Вы можете добавить gameObject в свою сцену Unity и создать MonoBehavior script, который содержит эти три метода (paySucess (строковое сообщение), payFailed (строковое сообщение) и payCancel (сообщение)). Затем присоедините новый созданный script к gameObject (допустим, что имя этого объекта игры будет "PayListener" ) и убедитесь, что объект gameObject существует в вашей сцене при выполнении Java-кода (вы можете вызвать DontDestroyOnLoad
в gameObject в Awake
, например).
Затем в вашем 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);
}
});
Это предотвратит успешное создание java файла без класса com.unity3d.player.UnityPlayer
. Вы можете найти файл /Applications/Unity/Unity.app/Contents/PlaybackEngines/AndroidPlayer/bin/classes.jar
и добавить его в качестве библиотеки зависимостей для вашего проекта java. Затем вы можете создавать, генерировать и использовать новый файл jar без ошибок.
Из-за метода UnitySendMessage
может принимать только один параметр, поэтому вам может понадобиться подключить псевдоним результата payFailed
и errorInt по стратегии и проанализировать его обратно на стороне Unity С#.
Используя новый встроенный файл jar и загружайте его как AndroidJavaClass
или AndroidJavaObject
, соответствующий метод в PayListener script должен вызываться, когда вызываемый вызов в Java вызывается.
Для документации плагина Android с UnitySendMessage вы можете посетить официальное руководство по внедрению плагина Android JNI здесь, в примере 3.
Ответ 2
Вы можете связать эти проекты вместе в 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.
}
}
}
С++/CLI Layer - проект DLL С++ с поддержкой CLR (/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 <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);
}
Ответ 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. Между дескрипторами полей в дескрипторе метода нет пробелов.
Помимо типа возврата void, который обозначается символом V, все другие типы возвращаемых данных используют дескриптор поля. В приведенной ниже таблице описывается декларация метода Java и соответствующий дескриптор JNI. Дескриптор метода JNI используется при вызове метода 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. После создания метод LoadJVM должен вызываться с параметрами JVM, а затем загружает виртуальную машину 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.
Либо вы можете получить больше на этой
Ответ 4
Посмотрите http://jni4net.sourceforge.net/. Я успешно использовал его для связи между CLR и JVM. Пример Java-приложения, который вызывает классы .NET, можно найти здесь. Также поддерживаются события (связанные с интерфейсами java-прослушивателя).
Ответ 5
Я решил решить эту проблему самостоятельно, другие ответы, размещенные здесь, но, к сожалению, не касались того факта, что проблема, с которой я столкнулся, была в Unity.
То, как я решил это, было написание пользовательского класса слушателя в Java с помощью функции GetResult, которая возвращала строку, объясняющую, каков был результат (т.е. какая функция была вызвана и с каким результатом), и функция get для результата который С# вызывается через AndroidJNI.
После совершения покупки вызов заключался в вызове функции get из С# до тех пор, пока я не получил результат.
Ответ 6
Звучит как беспорядок. Что вы пытаетесь сделать точно и почему? Почему бы просто не запускать JVM отдельно и CLR отдельно, интерфейс через вызовы REST или что-то подобное?