8

ネイティブ スレッド (pthreads) を使用して C++ アプリを作成していますが、いくつかの Java メソッドなどを呼び出す必要があります。別のスレッド。クラスのメソッドが異なるスレッドから呼び出される可能性がある場合、JNIEnv をキャッシュするのではなく、代わりに JavaVM をキャッシュし、現在のスレッドをアタッチして JNIEnv を取得する必要があることはわかっています。しかし、それはまた、JNIEnv から取得したものをキャッシュできないということですか? 次の JNIEnv メソッドで取得したオブジェクトを使用する必要があります。

FindClass、GetMethodID、NewObject、NewGlobalRef

それらはスレッド間で有効なままですか、それとも毎回新しいものを取得する必要がありますか? 後者の場合、1 つのネイティブ スレッドでオブジェクトを作成し、別のスレッドで同じオブジェクトにアクセスできる方法はありますか?

4

2 に答える 2

10

FindClassGetMethodID、などのJNI メソッドGetFieldIDは、JVM の存続期間にわたって同じ結果を生成することが保証されている高価な操作です。これらの操作には時間がかかるため、後でネイティブ側で再利用できるように結果をどこかに保存することをお勧めします (これがキャッシングです)。

JNI キャッシングは、これらの JNI 関数呼び出しのみを考慮します。他の C++ または Java オブジェクトをキャッシュしたい場合、これは別のトピックです。(ただ明確にします)。

キャッシュされたクラス、メソッド、およびフィールドは、取得元のスレッドに依存しないため、異なるスレッド間で有効です。Set<type>Fieldまたは を使用してオブジェクトのフィールドを取得または設定するときは、せいぜいスレッドセーフな操作を実行する必要がありますGet<type>Field

FindClass はクラスオブジェクトへのローカル参照を返すため、それを取得する関数が終了した後に確実に再利用できるように、それをグローバル参照に変換する必要があります。これは、NewGlobalReference を使用して実現できます。

jclass tmp_double_Class = env->FindClass( "java/lang/Double" ); // Check for exceptions!
double_Class = static_cast<jclass>( env->NewGlobalRef( tmp_double_Class ) );
if( double_Class == NULL )
  return;
env->DeleteLocalRef( tmp_double_Class );

ここに、すべての JNI キャッシング トピックの例があります。

MyJni.cpp:

// Just a shortcut for checking for exceptions
#define CHECK_JNI_EXCEPTION( JNIenv ) \
  if( JNIenv->ExceptionCheck() )\
  {\
    JNIenv->ExceptionClear();\
    return JNI_FALSE;\
  }\
\

// Global variables
jclass    point_Class;
jmethodID point_ctor_Method;
jfieldID  point_x_Field;
jfieldID  point_y_Field;

JNIEXPORT jboolean JNICALL Java_com_company_package_MyClass_nativeInit( JNIEnv * env,
                                                                        jclass   clazz )
{
  // Cache java.lang.Double class, methods and fields
  jclass tmp_point_Class = env->FindClass( "android/graphics/Point" );
  CHECK_JNI_EXCEPTION( env )
  point_Class = static_cast<jclass>( env->NewGlobalRef( tmp_point_Class ) );
  if( point_Class == NULL )
    return JNI_FALSE;
  env->DeleteLocalRef( tmp_point_Class );
  point_ctor_Method = env->GetMethodID( point_Class, "<init>", "(II)V" );
  CHECK_JNI_EXCEPTION( env )
  point_x_Field = env->GetFieldID( point_Class, "x", "I" );
  CHECK_JNI_EXCEPTION( env )
  point_y_Field = env->GetFieldID( point_Class, "y", "I" );
  CHECK_JNI_EXCEPTION( env )
  return JNI_TRUE;
}

MyJni.java:

package com.company.package;

class MyClass {
  // ... All java code here ... 

  // Trigger JNI Caching (could be also done using JNI_OnLoad...)
  private static native void nativeInit();

  static {
    System.loadLibrary( "mylib" );
    nativeInit(); // should check the result
  }
}

楽しむ ;)

于 2013-10-29T11:34:44.683 に答える
4

オブジェクトはスレッド固有ではありません。それらは最初は「ローカル」参照であり、コピーを保持したい場合は、「グローバル」参照を作成 (そして最終的には削除) することで、そうしていることを VM に伝える必要があります。

http://developer.android.com/training/articles/perf-jni.html、特に「ローカルおよびグローバル参照」セクションを参照してください。

于 2012-12-18T01:09:59.383 に答える