Получение допустимого указателя JNIEnv

У меня есть dll С++, который я хочу использовать в Unity, экспортируя функции на С#. Проект Unity работает на устройствах Android, а код С++ использует java. Для инициализации С++ мне нужно сначала вызвать следующую функцию:

void api_initialize(JNIEnv* env, jobject* app_context, jobject* class_loader) {

    JavaVM* vm = nullptr;
    env->GetJavaVM(&vm);
    if (!vm) {
      return;
    }

    //Do other proprietary things
}

В Unity у меня есть следующая экспортированная функция Dll

    [DllImport (dllName)]
    private static extern void api_initialize (IntPtr java_env, IntPtr app_context, IntPtr class_loader);

Мой вопрос Как получить указатель JNIEnv в моем классе С#, а затем передать его как параметр в эту функцию?

Я не создатель этого API и не имею доступа к его модификации, поэтому мне нужно получить JavaVM от JNIEnv, а не наоборот.

Ответы

Ответ 1

Я предполагаю, что вы не можете сделать это (возможно, там, но еще не видели его), потому что этот тип NDK calls требует java-упаковки вокруг, поэтому я хотел бы использовать другое решение, использующее Java как intermediate для ваших вызовов С#, а затем вы можете redirect выполнить этот вызов на NDK code с java.

using UnityEngine;
using System.Collections;
using System.IO;

#if UNITY_ANDROID
public class JavaCallPlugin {

  // Avoid invalid calls
  public static void initiateNDK() {
    if (Application.platform != RuntimePlatform.Android)
          return;

    var pluginClass = 
        new AndroidJavaClass("com.package.class.UnityJavaDelegate");
    AndroidJavaObject plugin = 
        pluginClass.CallStatic<AndroidJavaObject>("obj");
    return plugin.Call("javaCallToNDK");
  } 
}
#endif

И в вашем java файле сделайте это

package com.package.class;

import android.content.ContentValues;
import android.content.Intent;
import android.os.Environment;

public class UnityJavaDelegate{
  private static UnityJavaDelegate obj;

  // define function call to your NDK method with native keyword
  private native void api_initialize(); 

  static {
    System.loadLibrary("yourlibraryname"); // setup your ndk lib to use 
  }

  public static UnityJavaDelegate getObj() {
    if(m_instance == null)
       obj= new UnityJavaDelegate();
    return obj;
  }

  private UnityJavaDelegate(){
  }

  public void javaCallToNDK(){
    // this call may not work because i haven't passed class 
    // loader TO NDK before, if not then you should try links 
    // at the bottom of the post to know how to pass customize 
    // as parameter to NDK.

    // put your call to NDK function here 
    api_initialize(this.getClass().getClassLoader()); 
  }
}

Когда вы объявляете собственный метод, javah автоматически генерирует определение вызова ndk с помощью javaEnv и jobject в качестве параметра, поэтому я думаю, вам нужно только передать загрузчик классов здесь. Вы можете попробовать это link и в случае получения дополнительных разъяснений.

Удачи. Упование Это поможет.

Ответ 2

Я думаю, вы можете создать другой родной плагин, который даст вам JNIEnv.

Плагины для Android

Подводя итог этой статье, вы должны попробовать что-то вроде этого (непроверенный псевдокод):

JavaVM* g_JVM; // JavaVM is valid for all threads, so just save it globally 

extern "C" {
    JNIEnv* GetJniEnv();
}

// The VM calls JNI_OnLoad when the native library is loaded
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    g_JVM = vm;
    return JNI_VERSION_1_6;
}

// The JNI interface pointer (JNIEnv) is valid only in the current thread.
JNIEnv* GetJniEnv() {
    JNIEnv* jni_env = 0;
    g_JVM->AttachCurrentThread(&jni_env, 0);
    return jni_env;
}

Тогда в С#

[DllImport ("PluginName")]
private static extern IntPtr GetJniEnv();