1

JVM を既存の C++ アプリケーションに埋め込んでおり、ネイティブ Java 関数の実装をクラスに登録する必要があります。

ネイティブ関数を持つこの単純なクラスを考えてみましょう:

class Native {
  static {
    System.out.println("Class 'Native' static initializer called.");
  }

  public native int f(int i);
}

JVM 内で OSGi を実行しているため、JNI からクラスをロードするのではなく、Java コードを使用して (正しいクラス ローダーを使用して) クラスを取得する必要があります。ただし、この例を単純にするために、OSGi は省略しています。

jclassC++ で値を取得するために、次の 4 つの異なる Java メソッドがあります。

class Bridge {
  public Class<?> getNativeClass1() throws ClassNotFoundException {
    return getClass().getClassLoader().loadClass("org.example.Native");
  }

  public Class<?> getNativeClass2() throws ClassNotFoundException {
    return Class.forName("org.example.Native", false, getClass().getClassLoader());
  }

  public Class<?> getNativeClass3() throws ClassNotFoundException {
    final Class<?> clazz = getClass().getClassLoader()
                                     .loadClass("org.example.Native");
    clazz.getMethods();
    return clazz;
  }

  public Class<?> getNativeClass4() throws ClassNotFoundException {
    return Class.forName("org.example.Native", true, getClass().getClassLoader());
  }
}

C++ でネイティブ関数の実装を登録するには、次のNative.f()ように呼び出します。

JNIEnv* env = ...
jclass clazz = ...; // Calling one of the four methods above.
JNINativeMethod nativeMethod = {
  (char*) "f",      // Method name 'f'.
  (char*)  "(I)I;", // Signature 'int --> int'.
  (void*) f         // Pointer to C++ implementation of function.
};
env->RegisterNatives(clazz, &nativeMethod, 1);

Class<?>インスタンスを取得するために使用する方法に応じて、異なる結果が得られます。

  • getNativeClass1()Native: クラスをロードするとき (もちろん、クラスのインスタンスを作成するとき) に静的初期化子がクラスで実行されず、ネイティブ実装が正しくバインドされません。(Java でネイティブ関数を呼び出すと、正しくない結果が返されるか、JVM がクラッシュします。)
  • getNativeClass2(): 同上。
  • getNativeClass3()Native:クラスがロードされたときに静的イニシャライザはまだ呼び出されていませんが、ネイティブ実装正しくバインドされており、正常に呼び出すことができますf()
  • getNativeClass3():クラスがロードされ、ネイティブ実装正しくバインドされると、静的初期化子クラスで呼び出されます。Native

そのClassLoader.loadClass()ため、適切に初期化されず、適切に機能しないようにクラスをロードしているようJNIEnv::RegisterNatives()です。ただし、Class.getMethods()ネイティブ メソッドのバインドが機能するように、呼び出すと何らかの方法でクラスが初期化されます (静的初期化子を呼び出さずに)。

一方、初期化されていないインスタンスを返すのとClass.forName(clazz, false, classLoader)まったく同じように機能するようです。Class.loadClass()Class

誰でも違いを説明できますか

  1. getNativeClass1()andによって返されるような初期化されていないクラスgetNativeClass2()
  2. によって返されるような部分的に初期化されたクラスgetNativeClass3()
  3. によって返されるような完全に初期化されたクラスgetNativeClass4()

を呼び出す前にクラスをロードする最も移植性の高い方法は何JNIEnv::RegisterNatives()ですか?

4

1 に答える 1

3

そのため、 ClassLoader.loadClass() がクラスをロードするように見えるため、適切に初期化されていません

ドキュメントによると:

loadClass(String): "このメソッドを呼び出すことは、loadClass(name, false) を呼び出すことと同じです。"

loadClass(String,boolean)(強調追加): 「上記の手順を使用してクラスが見つかり、解決フラグが true の場合、このメソッドは、結果の Class オブジェクトで resolveClass(Class) メソッドを呼び出します」

これらの 2 つのメソッドは、ロードとリンクの間に何かを行う必要があるクラスローダーによる内部使用を目的としています。なぜloadClass(String)パブリックとマークされているのかはわかりませんが、そうすべきではないことは間違いありません。

そして、呼び出す前にクラスをロードする最も移植性の高い方法は何ですか

Class.forName()これは、コンテキスト クラスローダーを使用し、クラスが使用できる状態であることを保証します。

于 2013-01-14T15:08:24.167 に答える