缓存JNI对象和线程安全(在Android中)

rea*_*alh 3 java-native-interface thread-safety android-ndk jnienv

我正在编写一个带有本机线程(pthreads)的C++应用程序,我需要调用一些Java方法等.我不确定哪些JNI对象可以安全地缓存,即存储在我的C++对象中以供以后使用,可能/可能是由不同的线程.我知道如果我的类'方法可以被不同的线程调用,我不能缓存JNIEnv,而是缓存JavaVM并通过附加当前线程获得JNIEnv.但这是否意味着我无法缓存从JNIEnv获得的任何东西?我需要使用以下JNIEnv方法获得的对象:

FindClass,GetMethodID,NewObject,NewGlobalRef

那些在线程中保持有效,还是每次都必须获得新的?如果是后者,有没有办法在一个本机线程中创建一个对象,并且能够访问另一个线程中的同一个对象?

Sdr*_*dra 7

JNI方法,如FindClass,GetMethodID,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 );
Run Code Online (Sandbox Code Playgroud)

这里有一个所有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;
}
Run Code Online (Sandbox Code Playgroud)

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
  }
}
Run Code Online (Sandbox Code Playgroud)

玩得开心 ;)