正如其他人已经提到的,一种可能性是创建一个共享库 (.dll) 并从本机代码调用它和从 Java 来交换数据。
但是,如果您想回调在与 JVM 最初创建的模块相同的模块中的本地代码中定义的 C 函数,则可以使用 RegisterNatives。
简单示例
- C 程序创建 JVM
- 它调用一个类的 Main
- Java Main 在调用 C 代码中回调一个名为 connect0 的 C 函数
- 为了有一个测试用例,本机 C 函数构造一个 Java 字符串并返回它
- Java 端打印结果
Java
package com.software7.test;
public class Main {
private native String connect0() ;
public static void main(String[] args) {
Main m = new Main();
m.makeTest(args);
}
private void makeTest(String[] args) {
System.out.println("Java: main called");
for (String arg : args) {
System.out.println(" -> Java: argument: '" + arg + "'");
}
String res = connect0(); //callback into native code
System.out.println("Java: result of connect0() is '" + res + "'"); //process returned String
}
}
C 程序
可以用 C 语言创建 Java 虚拟机,如图所示 here
(不仅适用于 cygwin,还适用于 VS 2019),然后注册 RegisterNatives 本机 C 回调。因此,使用上面链接中的函数 invoke_class 可能如下所示:
#include <stdio.h>
#include <windows.h>
#include <jni.h>
#include <stdlib.h>
#include <stdbool.h>
...
void invoke_class(JNIEnv* env) {
jclass helloWorldClass;
jmethodID mainMethod;
jobjectArray applicationArgs;
jstring applicationArg0;
helloWorldClass = (*env)->FindClass(env, "com/software7/test/Main");
mainMethod = (*env)->GetStaticMethodID(env, helloWorldClass, "main", "([Ljava/lang/String;)V");
applicationArgs = (*env)->NewObjectArray(env, 1, (*env)->FindClass(env, "java/lang/String"), NULL);
applicationArg0 = (*env)->NewStringUTF(env, "one argument");
(*env)->SetObjectArrayElement(env, applicationArgs, 0, applicationArg0);
(*env)->CallStaticVoidMethod(env, helloWorldClass, mainMethod, applicationArgs);
}
jstring connect0(JNIEnv* env, jobject thiz);
static JNINativeMethod native_methods[] = {
{ "connect0", "()Ljava/lang/String;", (void*)connect0 },
};
jstring connect0(JNIEnv* env, jobject thiz) {
printf("C: connect0 called\n");
return (*env)->NewStringUTF(env, "Some Result!!");
}
static bool register_native_methods(JNIEnv* env) {
jclass clazz = (*env)->FindClass(env, "com/software7/test/Main");
if (clazz == NULL) {
return false;
}
int num_methods = sizeof(native_methods) / sizeof(native_methods[0]);
if ((*env)->RegisterNatives(env, clazz, native_methods, num_methods) < 0) {
return false;
}
return true;
}
int main() {
printf("C: Program starts, creating VM...\n");
JNIEnv* env = create_vm();
if (env == NULL) {
printf("C: creating JVM failed\n");
return 1;
}
if (!register_native_methods(env)) {
printf("C: registering native methods failed\n");
return 1;
}
invoke_class(env);
destroy_vm();
getchar();
return 0;
}
结果
链接
从 C 程序创建 JVM:http://www.inonit.com/cygwin/jni/invocationApi/c.html
注册本机方法:https://docs.oracle.com/en/java/javase/11/docs/specs/jni/functions.html#registering-native-methods