All examples I've found so far for calling a Java function from C, show how to first create/start a JVM from C. But In my case, the application starts from Java and the C function is called from Java.
Is it possible, for instance, to create a "native" method implemented in C, and to use it in order to save the JNIEnv pointer, and then to reuse it, instead of creating a new JVM instance from C, in order to call Java methods?
Is there an example for it?
EDIT:
The question above is based on a misunderstanding. Every native method (A Java method implemented in C) is supplied with an up-tp-date JNIEnv pointer as a part of its signature. This is the only JNIEnv pointer that should be used for calling Java methods from a native method.
Caching and using an old saved JNIEnv pointer could in the best scenario crash your application. In worse scenarios, it might cause low-level inconsistency problems that are very hard to debug and understand.
As JJF stated, this is definitely possible! I'll show an example below, assuming you already know how to call Java methods from C, but without having to create a JVM!
First, we have a Java class test.Test.java that calls the native method methodA:
package test;
public class Test {
static {
System.loadLibrary("test");
}
private native void methodA();
public static void methodB() {
System.out.println("Java: Method B has executed!");
}
public static void main(String[] args) {
new Test().methodA();
}
}
Next, we have the header file created by javah or javac -h:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class test_Test */
#ifndef _Included_test_Test
#define _Included_test_Test
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: test_Test
* Method: methodA
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_test_Test_methodA
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
Below is the C file which holds the native method methodA:
#include <jni.h>
#include <stdio.h>
#include "test_Test.h"
void callMethodB(JNIEnv *env);
JNIEXPORT void JNICALL Java_test_Test_methodA(JNIEnv *env, jobject thisObj) {
printf("C: Method A executed!\n"); fflush(stdout);
callMethodB(env);
return;
}
void callMethodB(JNIEnv *env) {
jclass testClass = (*env) -> FindClass(env, "test/Test");
jmethodID methodB = (*env) -> GetStaticMethodID(env, testClass, "methodB", "()V");
(*env) -> CallStaticVoidMethod(env, testClass, methodB, NULL);
return;
}
The output of this program is:
C: Method A executed!
Java: Method B has executed!
Remember to create a test.dll file with your preferred GNU compiler.
Instead of creating a new Java VM (which isn't possible anyway when the current process already has one) you can get the existing Java VM with JNI_GetCreatedJavaVMs (https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html#JNI_GetCreatedJavaVMs). Use AttachCurrentThread (https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html#AttachCurrentThread) to get an env pointer.
This will also work in a multithreaded environment, while passing env pointers around will only work in a single thread.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With