Non vi sono costruiti -in metodi o procedure specifiche, ma qui è un'implementazione alquanto standard, pulita e ripetibile che mostra come pratico le raccomandazioni di IBM.
Ho intenzione di assumere che tu stia chiamando la tua DLL da Java e la stai usando ciclicamente.
La classe nativa Java è org.stackoverflow.jni.NativeClazz
; si useranno anche i metodi integrati 2 JNI JNI_OnLoad
e JNI_OnUnload
(l'ex esempio ha utilizzato 2 metodi personalizzati: initialize()
e destroy()
).
vuoto JNI_OnLoad (JavaVM * vm, void * riservato): Questo metodo verrà utilizzato per registrare gli ID di classe come variabili globali e assegnare gli ID di metodo e gli ID campo per variabili statiche. Viene chiamato automaticamente quando il tuo driver viene caricato dalla Java VM e viene richiamato solo una volta durante il ciclo di vita.
vuoto JNI_OnUnload (JavaVM * vm, void * riservato): Questo metodo sarà utilizzato per liberare le variabili globali registrati da JNI_OnLoad
. La VM chiama JNI_OnUnload
immediatamente prima della chiusura dell'applicazione.
Motivazione: È a mia conoscenza che è necessario registrare gli ID di classe come riferimenti globali per mantenere la validità di qualsiasi ID metodo/ID campo correlato. Se non lo hai fatto e la classe viene scaricata dalla JVM, gli ID metodo/ID campo potrebbero cambiare in ricarica. Gli ID metodo e ID campo non devono essere registrati come riferimenti globali se l'ID classe associato è registrato. La registrazione degli ID di classe come riferimenti globali impedisce la disconnessione della classe Java associata, stabilizzando quindi i valori ID metodo/ID campo. Come riferimenti globali, gli ID di classe devono essere rimossi nel metodo destroy()
.
ID metodo e ID campo non sono gestiti dal codice nativo; sono gestiti dalla macchina virtuale e sono validi fino a quando la classe associata non viene scaricata. ID campo e ID metodo non possono essere eliminati in modo esplicito prima che la macchina virtuale scarichi la classe di definizione; possono essere lasciati alla VM da gestire dopo lo scaricamento.
codice di esempio
codice segue; i commenti nel file .cpp
spiegano la registrazione delle variabili a livello globale.
Ecco la classe Java BeanObject
che rappresenta il nostro oggetto di dati:
package org.stackoverflow.data;
public class BeanObject {
String foo = "";
public String getFoo() {
return foo;
}
}
Ecco uno scheletro classe 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
* instantiated Java classes.
*/
public native void instanceNativeMethod(BeanObject bean);
}
Qui viene generato il C++ file di intestazione utilizzando javah
su NativeClazz
:
/* 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
Ecco il file C++ cpp attuare il file di intestazione:
#include "org_stackoverflow_jni_NativeClazz.h"
using namespace std;
/**************************************************************
* Static Global Variables to cache Java Class and Method IDs
**************************************************************/
static jclass JC_BeanObject;
static jmethodID JMID_BeanObject_getFoo;
/**************************************************************
* 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);
}
Una delle migliori risposte su caching jclass/jmethodId/jfieldId! Ancora una domanda mi infastidisce. Che dire di jmethodID/jfieldID? Sono strutture opache, come possiamo liberarle/cancellarle o dovremmo? O semplicemente puntare tutto (tutti i ref rifuiti globali, jmethodIDs, jfieIDs) a NULL/nullptr è sufficiente? –
@KonstantinBerkow - Gli ID metodo e ID campo non sono gestiti dal codice nativo; sono gestiti dalla macchina virtuale e sono validi fino a quando la classe associata non viene scaricata. ID campo e ID metodo non possono essere eliminati in modo esplicito prima che la macchina virtuale scarichi la classe di definizione. Lascia che sia la VM a gestirli. Se ti fa sentire meglio impostarli su NULL, fallo dopo aver rilasciato la classe associata, ma non è affatto necessario. – JoshDM