Data Interaction Between Java and C++ Using JNI

The process begins by defining a Java class containing methods declared with the native keyword. After compiling the Java source file, the javah utility generates a C/C++ header file. Java declaration: ``` public native void showGreeting();


Generated JNI function signature: ```
JNIEXPORT void JNICALL Java_Greeter_showGreeting(JNIEnv* env, jobject obj);

Breaking down the function signature: - JNIEXPORT: Marks the function as exported for JNI access.

  • JNICALL: Specifies the JNI calling convention.
  • Java: Prefix indicating the Java language origin.
  • Greeter: The containing Java class name.
  • showGreeting: The actual method name.
  • JNIEnv*: Pointer providing access to JNI utility functions.
  • jobject: Handle to the calling Java object instance, akin to this.

Type Conversion

Transferring string data between Java and C++ requires explicit conversion. A common practice involves wrapping these conversions in helper classes for automatic resource management. Converting jstring to char*``` const char* GetStringUTFChars(jstring str, jboolean* isCopy); void ReleaseStringUTFChars(jstring str, const char* chars);


The `GetStringUTFChars` function returns a pointer to a modified UTF-8 string. The `isCopy` parameter indicates whether the JVM created a copy of the string data. Once processing is complete, `ReleaseStringUTFChars` notifies the JVM that the native code no longer needs the string data. **Converting char\* to jstring**```
jstring NewStringUTF(const char* utf);

This function allocates a new Java String object containing a copy of the provided UTF-8 characters. When returning a string to Java, this method is essential. The DeleteLocalRef function can release the local reference when no longer needed. ### Calling C++ from Java

When Java invokes a native method, execution transfers to the corresponding JNI function. Implementing the native function with calls to existing C++ logic completes the inetgration. ### Calling Java from C++

To invoke Java methods from native code, several JNI constructs are required: - jmethodID: Stores the identifier for a Java method.

  • CallVoidMethod: Invokes a method returning void.
  • AttachCurrentThread: Binds a native thread to the JVM.
  • DetachCurrentThread: Releases a native thread from the JVM.

When working with threads not spawned by the JVM, attaching to the current thread is mandatory: ``` JNIEnv* env; g_javaVM->GetEnv((void**)&env, JNI_VERSION_1_6);

bool wasAttached = false; if (env == nullptr) { int result = g_javaVM->AttachCurrentThread(&env, nullptr); if (result != JNI_OK) { return; } wasAttached = true; }

jstring jstrParam = env->NewStringUTF(paramValue.c_str()); env->CallVoidMethod(callbackObj, methodId, jstrParam);

if (wasAttached) { g_javaVM->DetachCurrentThread(); }


Before invoking a method, the native layer must cache references to the Java object and method ID: ```
callbackObject = env->NewGlobalRef(javaCallback);
callbackClass = reinterpret_cast<jclass>(env->NewGlobalRef(env->GetObjectClass(callbackObject)));
callbackMethodId = env->GetMethodID(callbackClass, "onDataUpdate", "(Ljava/lang/String;I)V");

Using NewGlobalRef ensures the references persist across JNI calls, preventing premature garbage collection.

Tags: JNI java C++ Native Interface Cross-language Integration

Posted on Sat, 08 Aug 2026 16:44:11 +0000 by ozzy