I read on on IBM that
To access Java objects\' fields and invoke their methods, native code must make calls to FindClass(), GetFieldID(), GetMethod
Here is how I practice IBM's recommendation. Considering demo java class like that:
public class SimpleClazz {
public int value = 10;
public native int getValue();
static {
// Load Native Library
System.loadLibrary("The native library name");
}
}
The corresponding jni header file like that:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class SimpleClazz */
#ifndef _Included_SimpleClazz
#define _Included_SimpleClazz
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: SimpleClazz
* Method: getValue
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_SimpleClazz_getValue
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
According to IBM's recommendation, we need to cache the used class SimpleClazz and the field id of the object member value.
After learning this good article, I cache the SimpleClazz in the function JNI_OnLoad, which is called when the native library is loaded (for example, through System.loadLibrary). In the JNI_Onload, we do find class and store this jclass as a global field.
Furthermore, in the native implementation of getValue, we use static local variable to cache the field id of value. This design is to make sure this filed id can be in the better scope, rather than in the global scope. The drawback of this design is that we need to compare with NULL each time we call this function. I learnt this design from section 4.4.1 of the book The Java Native Interface: Programmer's Guide and Specification.
Finally, we also need to write the function JNI_OnUnload, which is called when the class loader containing the native library is garbage collected. In this function, we release the global reference of jclass.
My cpp implementation is shown as below:
#include
#include
static jclass simpleCls;
// According to http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/invocation.html#JNI_OnLoad
// The VM calls JNI_OnLoad when the native library is loaded (for example, through System.loadLibrary).
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
} else {
jclass localSimpleCls = (*env)->FindClass("SimpleClazz");
if (localSimpleCls == NULL) {
return JNI_ERR;
}
simpleCls = (jclass) (*env)->NewGlobalRef(env, localSimpleCls);
}
return JNI_VERSION_1_6;
}
JNIEXPORT jint JNICALL Java_SimpleClazz_getValue(JNIEnv * env, jobject thiz){
static jfieldID valueID = NULL;
if (valueID == NULL) {
valueID = (*env)->GetFieldID(env, simpleCls, "value", "I");
if (valueID == NULL){
return JNI_ERR; // Exception thrown
}
}
jint value = (*env)->GetIntField(env, thiz, valueID);
return value;
}
// According to http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/invocation.html#JNI_OnUnload
// The VM calls JNI_OnUnload when the class loader containing the native library is garbage collected.
void JNI_OnUnload(JavaVM *vm, void *reserved) {
JNIEnv* env;
if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6) != JNI_OK) {
// Something is wrong but nothing we can do about this :(
return;
} else {
if (0 != NULL){
(*env)->DeleteGlobalRef(env, simpleCls);
}
}
}