26

私はIBMでそれを読みました

Java オブジェクトのフィールドにアクセスしてそのメソッドを呼び出すには、ネイティブ コードで FindClass()、GetFieldID()、GetMethodId()、および GetStaticMethodID() を呼び出す必要があります。GetFieldID()、GetMethodID()、および GetStaticMethodID() の場合、特定のクラスに対して返される ID は、JVM プロセスの存続期間中は変更されません。ただし、フィールドやメソッドを取得するための呼び出しには、JVM でかなりの作業が必要になる場合があります。これは、フィールドやメソッドがスーパークラスから継承されている可能性があり、JVM がそれらを見つけるためにクラス階層を上っていくためです。特定のクラスの ID は同じであるため、一度検索してから再利用する必要があります。同様に、クラス オブジェクトの検索にはコストがかかる可能性があるため、それらもキャッシュする必要があります。

、、、およびオブジェクトを JNIにキャッシュするmethodIDにはどうすればよいですか? 組み込みのメソッドや従わなければならない特定の手順はありますか?fieldIDclass

4

5 に答える 5

51

従うべき組み込みの方法論はありませんが、IBM の推奨事項を私がどのように実践しているかを示す、やや標準的でクリーンで反復可能な実装を次に示します。

Java から DLL を呼び出しており、アプリケーションのライフサイクル全体で何度も参照していると仮定します。

サンプルのネイティブ 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): このメソッドは、によって登録されたすべてのグローバル変数を解放するために使用されJNI_OnLoad()ます。JNI_OnUnload()VM は、アプリケーションのシャットダウンの直前に自動的に呼び出します。

根拠:関連するメソッド ID/フィールド ID の実行可能性を維持するために、クラス ID をグローバル参照として登録する必要があることを理解しています。これが行われず、クラスが JVM からアンロードされた場合、クラスのリロード時に、メソッド ID / フィールド ID が異なる場合があります。クラス ID がグローバル参照として登録されている場合、関連するメソッド ID とフィールド ID をグローバル参照として登録する必要はありません。クラス ID をグローバル参照として登録すると、関連する Java クラスがアンロードされなくなり、メソッド ID / フィールド ID の値が安定します。クラス ID を含むグローバル参照は、 で削除する必要がありますJNI_OnUnload()

メソッド ID とフィールド ID はネイティブ コードでは管理されません。それらは仮想マシンによって管理され、関連するクラスがアンロードされるまで有効です。仮想マシンが定義クラスをアンロードする前に、フィールド ID とメソッド ID を明示的に削除することはできません。アンロード後に VM が処理できるようにしておくことができます。

サンプルコード

次の C++ コード セクションのコメントでは、変数をグローバルに登録する方法について説明しています。

BeanObject以下は、データ オブジェクトを表すJava クラスです。

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);
}

onorg_stackoverflow_jni_NativeClazz.hを使用して生成された C++ ヘッダー ファイル " " は次のとおりです。javahNativeClazz

/* 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);
}
于 2012-12-18T20:12:50.023 に答える
8

以下は、IBM の推奨事項を実践する方法です。そのようなデモJavaクラスを検討してください:

public class SimpleClazz {

    public int value = 10;

    public native int getValue();

    static {
        // Load Native Library
        System.loadLibrary("The native library name");
    }
}

そのような対応するjniヘッダーファイル:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* 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

IBM の推奨に従って、使用されたクラスSimpleClazzとオブジェクト member のフィールド idをキャッシュする必要がありますvalue

この優れた記事を学んだ後SimpleClazz、関数にキャッシュします。この関数JNI_OnLoadは、ネイティブ ライブラリが読み込まれるときに呼び出されます (たとえば、System.loadLibrary を介して)。では、JNI_Onloadクラスを見つけて、この jclass をグローバル フィールドとして保存します。

さらに、 のネイティブ実装ではgetValue、静的ローカル変数を使用して のフィールド ID をキャッシュしますvalue。この設計は、このフィールド ID がグローバル スコープではなく、より適切なスコープにあることを確認するためのものです。この設計の欠点は、この関数を呼び出すたびに NULL と比較する必要があることです。この設計は、 The Java Native Interface: Programmer's Guide and Specificationという本のセクション 4.4.1 から学びました。

最後にJNI_OnUnload、ネイティブ ライブラリを含むクラス ローダーがガベージ コレクションされるときに呼び出される関数も記述する必要があります。この関数では、jclass のグローバル参照を解放します。

私の cpp 実装は次のようになります。

#include <jni.h>
#include <SimpleClazz.h>

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);
        }
    }
}
于 2015-02-15T13:16:18.593 に答える
2

次のようなユーティリティ構造を使用できます。

typedef struct MYVARIANT_FID_CACHE {
    int cached;
    jclass clazz;
    jfieldID pAddress;
} MYVARIANT_FID_CACHE;

MYVARIANT_FID_CACHE VARIANTFc;

void cacheMYVARIANTFields(JNIEnv *env, jobject lpObject)
{
    if (VARIANTFc.cached) return;
    VARIANTFc.clazz = env->GetObjectClass(lpObject);
    VARIANTFc.pAddress = env->GetFieldID(VARIANTFc.clazz, "pAddress", "I");
    VARIANTFc.cached = 1;
}

VARIANT *getMYVARIANTFields(JNIEnv *env, jobject lpObject, VARIANT *lpStruct)
{
    if (!VARIANTFc.cached) cacheVARIANT2Fields(env, lpObject);

    lpStruct = (VARIANT*)(env->GetIntField(lpObject, VARIANTFc.pAddress));

    return lpStruct;
}

これは私の質問から取られています: https://stackoverflow.com/questions/10617714/how-to-extend-swt-com-support

いくつかの良い例についてos_structs.cは、Eclipse SWT 実装にバンドルされているを見てください。

注:上記のコードは単なる例であり、さまざまな OS に適合させることができます。また、「Javaフィールドにアクセスする方法」を示しているだけです。メソッドについては、同じアプローチに従うことができます。

于 2012-05-16T12:11:54.353 に答える
0

単純なケースとして、Java クラスから呼び出される initId メソッドで初期化された C コードで静的フィールドを使用します。

package demo;
public class WithNatives {

 static {
   initIDs();
 }

 private static native void initIDs();

}

そしてCで:

static jmethodID methodId;

void JNICALL Java_demo_WithNatives_initIDs(JNIEnv *env, jclass clazz)
{
   // initialize methodId
}
于 2012-05-21T08:09:00.900 に答える