没有内置的方法可以遵循,但是这里有一个有点标准,干净和可重复的实现,展示了我如何实践IBM的建议。
我将假设您正在从 Java 调用 DLL,并且您在整个应用程序生命周期中多次引用它。
示例 Native Java 类被命名为 ,它将实现 2 个内置的 JNI 方法和 。org.stackoverflow.jni.NativeClazz
JNI_OnLoad()
JNI_OnUnload()
void JNI_OnLoad(JavaVM *vm, void *reserved)
:此方法将用于将类 ID 注册为全局变量,并将方法 ID 和字段 ID 分配给静态变量。当驱动程序由 Java VM 加载时,将自动调用该方法。在驱动程序生命周期中,它只被调用一次。
void JNI_OnUnload(JavaVM *vm, void *reserved)
:此方法将用于释放 由 注册的任何全局变量。VM 将在应用程序关闭之前立即自动调用。JNI_OnLoad()
JNI_OnUnload()
理由:我的理解是,类 ID 必须注册为全局引用,以保持任何关联的方法 ID/字段 ID 的可行性。如果未执行此操作,并且从 JVM 中卸载了类,则在类重新装入时,方法 ID/字段 ID 可能会有所不同。如果将类 ID 注册为全局引用,则不需要将关联的方法 ID 和字段 ID 注册为全局引用。将类 ID 注册为全局引用可防止卸载关联的 Java 类,从而稳定方法 ID/字段 ID 值。应在 中删除全局引用,包括类 ID。JNI_OnUnload()
方法 ID 和字段 ID 不是由本机代码管理的;它们由虚拟机管理,在卸载关联的类之前一直有效。在虚拟机卸载定义类之前,无法显式删除字段 ID 和方法 ID;它们可以留给 VM 在卸载后处理。
示例代码
以下C++代码部分中的注释说明了全局注册变量。
下面是表示数据对象的 Java 类 BeanObject
:
package org.stackoverflow.data;
public class BeanObject {
String foo = "";
public String getFoo() {
return foo;
}
}
下面是一个框架 Java 类 NativeClazz
:
package org.stackoverflow.jni;
import org.stackoverflow.data.BeanObject;
public class NativeClazz {
// Static area for forced initialization
static {
// Load Native Library (C++); calls JNI_OnLoad()
System.loadLibrary("Native_Library_File_Name");
}
/**
* A static native method you plan to call.
*/
public static native void staticNativeMethod(BeanObject bean);
/**
* A non-static native method you plan to call, to show this also works with
* Java class instances.
*/
public native void instanceNativeMethod(BeanObject bean);
}
以下是在 NativeClazz
上使用 javah
生成的C++头文件“org_stackoverflow_jni_NativeClazz.h
”:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class org_stackoverflow_jni_NativeClazz */
#ifndef _Included_org_stackoverflow_jni_NativeClazz
#define _Included_org_stackoverflow_jni_NativeClazz
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: org_stackoverflow_jni_NativeClazz_staticNativeMethod
* Method: staticNativeMethod
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_org_stackoverflow_jni_NativeClazz_staticNativeMethod
(JNIEnv *, jclass, jobject);
/*
* Class: org_stackoverflow_jni_NativeClazz_instanceNativeMethod
* Method: instanceNativeMethod
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_org_stackoverflow_jni_NativeClazz_instanceNativeMethod
(JNIEnv *, jobject, jobject);
#ifdef __cplusplus
}
#endif
#endif
下面是实现头文件C++ .cpp文件:
#include "org_stackoverflow_jni_NativeClazz.h"
using namespace std;
/**************************************************************
* Static Global Variables to cache Java Class and Method IDs
**************************************************************/
static jclass JC_BeanObject; // declare for each class
static jmethodID JMID_BeanObject_getFoo; // declare for each class method
/**************************************************************
* Declare JNI_VERSION for use in JNI_Onload/JNI_OnUnLoad
* Change value if a Java upgrade requires it (prior: JNI_VERSION_1_6)
**************************************************************/
static jint JNI_VERSION = JNI_VERSION_1_8;
/**************************************************************
* Initialize the static Class and Method Id variables
**************************************************************/
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
// Obtain the JNIEnv from the VM and confirm JNI_VERSION
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION) != JNI_OK) {
return JNI_ERR;
}
// Temporary local reference holder
jclass tempLocalClassRef;
// STEP 1/3 : Load the class id
tempLocalClassRef = env->FindClass("org/stackoverflow/data/BeanObject");
// STEP 2/3 : Assign the ClassId as a Global Reference
JC_BeanObject = (jclass) env->NewGlobalRef(tempLocalClassRef);
// STEP 3/3 : Delete the no longer needed local reference
env->DeleteLocalRef(tempLocalClassRef);
// Load the method id
JMID_BeanObject_getFoo = env->GetMethodID(JC_BeanObject, "getFoo", "(Ljava/lang/String;)V");
// ... repeat prior line for any other methods of BeanObject
// ... repeat STEPS 1-3 for any other classes; re-use tempLocalClassRef.
// Return the JNI Version as required by method
return JNI_VERSION;
}
/**************************************************************
* Destroy the global static Class Id variables
**************************************************************/
void JNI_OnUnload(JavaVM *vm, void *reserved) {
// Obtain the JNIEnv from the VM
// NOTE: some re-do the JNI Version check here, but I find that redundant
JNIEnv* env;
vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION);
// Destroy the global references
env->DeleteGlobalRef(JC_BeanObject);
// ... repeat for any other global references
}
/**************************************************************
* A Static Native Method
**************************************************************/
JNIEXPORT void JNICALL Java_org_stackoverflow_jni_NativeClazz_staticNativeMethod
(JNIEnv * env, jclass clazz, jobject jBeanObject) {
// Retrieve jstring from the Java Object
jstring jFoo = (jstring)env->CallObjectMethod(jBeanObject, JMID_BeanObject_getFoo);
// Make accessible to C++
const char * cFoo = env->GetStringUTFChars(jFoo, NULL);
// Do something with cFoo...
// Release Resources
env->ReleaseStringUTFChars(jFoo, cFoo);
env->DeleteLocalRef(jFoo);
}
/**************************************************************
* Instance / Non-Static Native Method
**************************************************************/
JNIEXPORT void JNICALL Java_org_stackoverflow_jni_NativeClazz_instanceNativeMethod
(JNIEnv * env, jobject selfReference, jobject jBeanObject) {
// Retrieve jstring from the Java Object
jstring jFoo = (jstring)env->CallObjectMethod(jBeanObject, JMID_BeanObject_getFoo);
// Make accessible to C++
const char * cFoo = env->GetStringUTFChars(jFoo, NULL);
// Do something with cFoo...
// Release Resources
env->ReleaseStringUTFChars(jFoo, cFoo);
env->DeleteLocalRef(jFoo);
}