How to embed JAVA in C++

How to embed JAVA in C++

Recently adding JAVA bindings for AWTK , it works fine under Windows and Linux, but it runs into the following problems on MACOS:

java[5714:260503] WARNING: NSWindow drag regions should only be invalidated on the Main Thread! This will throw an exception in the future. Called from (
	0   AppKit                              0x00007fff50d21607 -[NSWindow(NSWindow_Theme) _postWindowNeedsToResetDragMarginsUnlessPostingDisabled] + 378
	1   AppKit                              0x00007fff50d1e9f7 -[NSWindow _initContent:styleMask:backing:defer:contentView:] + 1479
	2   AppKit                              0x00007fff50d1e42a -[NSWindow initWithContentRect:styleMask:backing:defer:] + 45
	3   AppKit                              0x00007fff50fead08 -[NSWindow initWithContentRect:styleMask:backing:defer:screen:] + 52
	4   libSDL2-2.0.dylib                   0x0000000109947d87 Cocoa_CreateWindow + 887
	5   libSDL2-2.0.dylib                   0x000000010990aa46 SDL_CreateWindow_REAL + 1190
	6   libawtk-jni.dylib                   0x00000001225d7ad6 native_window_create_internal + 150
	7   libawtk-jni.dylib                   0x00000001225d7cc2 native_window_sdl_init + 162
	8   libawtk-jni.dylib                   0x00000001225da166 main_loop_init + 38
	9   libawtk-jni.dylib                   0x000000012251f866 tk_init + 262
	10  libawtk-jni.dylib                   0x0000000122505519 Java_awtk_AWTK_init + 57
	11  ???                                 0x0000000109b359f4 0x0 + 4457716212
	12  ???                                 0x0000000109b25d00 0x0 + 4457651456
)

It seems to be saying that AWTK is not running on the UI thread, so SDL cannot be initialized. After searching for a long time, I couldn't find a solution. After thinking about it, can I embed JAVA into C/C++ like a scripting language?

I searched on the Internet, maybe this usage is too weird, I only found a few examples, and none of them can be run directly. It took a lot of time to adjust. Make a note here for reference.

1. Parameters for starting the virtual machine

The basic parameters require two:

  • The path to the JNI dynamic library is set via java.library.path.
  • The program jar file is set via java.class.path.
static string toClassPath(const string& program) {
  return string("-Djava.class.path=") + program;
}

...

jvmopt[0].optionString = (char*)"-Djava.library.path=./lib";
jvmopt[1].optionString = (char*)classPath.c_str()

2. Call the main function

Here I need to pass the width and height parameters to the java program through the main function.

static jobjectArray prepareProgramArgs(JNIEnv* env, const char* w, const char* h) {
    jobjectArray ret= (jobjectArray)env->NewObjectArray(2,
          env->FindClass("java/lang/String"),
          env->NewStringUTF(""));

    env->SetObjectArrayElement(ret,0, env->NewStringUTF(w));
    env->SetObjectArrayElement(ret,1, env->NewStringUTF(h));

    return ret;
}

...

    jmethodID methodId = env->GetStaticMethodID(jcls, "main", "([Ljava/lang/String;)V");
    if (methodId != NULL) {
      jobjectArray args = prepareProgramArgs(env, w, h);

      env->CallStaticVoidMethod(jcls, methodId, args);

      if (env->ExceptionCheck()) {
        env->ExceptionDescribe();
        env->ExceptionClear();
      }
    }

3. Complete code

#include <jni.h>
#include <string>
#include <iostream>

using namespace std;

static string toClassName(const string& program) {
  string className;
  size_t start = program.find_last_of('/');
  size_t end = program.find_last_of('.');

  if(start == std::string::npos) {
    start = program.find_last_of('\\');
    if(start == std::string::npos) {
      start = -1;
    }
  }

  className = program.substr(start+1, end - start - 1);

  return className;
}
  
static string toClassPath(const string& program) {
  return string("-Djava.class.path=") + program;
}


static jobjectArray prepareProgramArgs(JNIEnv* env, const char* w, const char* h) {
    jobjectArray ret= (jobjectArray)env->NewObjectArray(2,
          env->FindClass("java/lang/String"),
          env->NewStringUTF(""));

    env->SetObjectArrayElement(ret,0, env->NewStringUTF(w));
    env->SetObjectArrayElement(ret,1, env->NewStringUTF(h));

    return ret;
}

int main(int argc, char** argv) {
  JavaVM* javaVM;
  JNIEnv* jniEnv;
  string program;
  string classPath;
  string className;
  const char* w = "320";
  const char* h = "480";
  JavaVMInitArgs vmArgs;
  JavaVMOption jvmopt[2];

  if(argc < 2) {
    printf("Usage: %s jar [w] [h]\n", argv[0]);

    return 0;
  }

  if(argc > 2) {
    w = argv[2];
  }
  
  if(argc > 3) {
    h = argv[3];
  }

  program = argv[1];
  className = toClassName(program);
  classPath = toClassPath(program);
  jvmopt[0].optionString = (char*)"-Djava.library.path=./lib";
  jvmopt[1].optionString = (char*)classPath.c_str();

  vmArgs.options = jvmopt;
  vmArgs.version = JNI_VERSION_1_8;
  vmArgs.ignoreUnrecognized = JNI_TRUE;
  vmArgs.nOptions = sizeof(jvmopt) / sizeof(jvmopt[0]);

  long flag = JNI_CreateJavaVM(&javaVM, (void**)&jniEnv, &vmArgs);
  if (flag == JNI_ERR) {
    cout << "Error creating VM. Exiting...\n";
    return 1;
  }

  JNIEnv* env = jniEnv;
  jclass jcls = env->FindClass(className.c_str());
  if (jcls == NULL) {
    jniEnv->ExceptionDescribe();
    javaVM->DestroyJavaVM();
    return 1;
  }

  if (jcls != NULL) {
    jmethodID methodId = env->GetStaticMethodID(jcls, "main", "([Ljava/lang/String;)V");
    if (methodId != NULL) {
      jobjectArray args = prepareProgramArgs(env, w, h);

      env->CallStaticVoidMethod(jcls, methodId, args);

      if (env->ExceptionCheck()) {
        env->ExceptionDescribe();
        env->ExceptionClear();
      }
    } else {
      cout << "Not found main, Exiting...\n";
    }
  } else {
    cout << "Not found class , Exiting...\n";
  }

  javaVM->DestroyJavaVM();
  return 0;
}

Fourth, compile and link

  • JAVA_HOME
/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home
  • Header file path.
$JAVA_HOME/include
$JAVA_HOME/include/darwin
  • The path to the library.
$JAVA_HOME/jre/lib/server
  • The name of the library. libjvm

  • the path to the runtime library

export DYLD_LIBRARY_PATH="$JAVA_HOME/jre/lib/server"

Five, command function parameters

The compilation passed, I thought it was done, but it prompted that the JRE could not be found when running. It turns out that the JDK installation is not enough, and the JRE needs to be installed separately. After the installation, the AWTK display is normal.

./bin/awtkRun bin/DemoButton.jar
{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324069889&siteId=291194637