Как передать класс 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 или что-то подобное?