缓存 JNI 对象和线程安全(在 Android 中) [英] Caching JNI objects and thread-safety (in Android)

查看:52
本文介绍了缓存 JNI 对象和线程安全(在 Android 中)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm writing a C++ app with native threads (pthreads) and I need to call some Java methods etc. I'm not sure which JNI objects can be safely cached ie stored in my C++ object for use later, possibly/probably by a different thread. I do know that if my class' methods can be called by different threads I mustn't cache the JNIEnv, but instead cache the JavaVM and get a JNIEnv by attaching the current thread. But does that also mean I can't cache anything obtained from a JNIEnv? I need to use the objects obtained by the following JNIEnv methods:

FindClass、GetMethodID、NewObject、NewGlobalRef

FindClass, GetMethodID, NewObject, NewGlobalRef

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

Do those stay valid across threads, or do I have to get new ones every time? If the latter, is there a way to create an object in one native thread and be able to access the same object in a different thread?

推荐答案

FindClassGetMethodIDGetFieldID 等 JNI 方法是昂贵的操作,保证在 JVM 的整个生命周期内生成相同的结果.由于这些操作非常耗时,因此明智的做法是将结果存储在某个地方以便稍后在本机端重用(这就是 缓存).

JNI methods like FindClass, GetMethodID, GetFieldID are expensive operation that are guaranteed to generate the same result over the life of the JVM. Since these operations are time consuming, it is wise to store the result somewhere to be reused later on in the native side (this is caching).

JNI 缓存只考虑这些 JNI 函数调用.如果您想缓存任何其他 C++ 或 Java 对象,这是一个不同的主题.(只是为了清楚).

JNI caching regards only these JNI function calls. If you want to cache any other C++ or Java object this is a different topic. (Just to be clear).

缓存的类、方法和字段不依赖于从中检索它们的线程,因此它们在不同的线程中有效.在使用 Set<type>FieldGet<type>Field 获取或设置某些对象的字段时,您最多必须执行线程安全操作.

The cached classes, methods and fields do not depend on the thread they are retrieved from, so they are valid across different threads. At most you have to perform thread safe operations when getting or setting some object's field with Set<type>Field or Get<type>Field.

由于 FindClass 返回一个对 class 对象的本地引用,因此您必须将其转换为全局引用,以保证在检索它的函数结束后重用它.您可以通过使用 NewGlobalReference 来实现:

Since FindClass returns a local reference to the class object, you have to turn it into a global reference to guarantee its reuse after the function that retrieves it ends. You can achieve this by using 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 缓存主题的示例:

Here you have an example of the all JNI Caching topic:

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:

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

玩得开心;)

这篇关于缓存 JNI 对象和线程安全(在 Android 中)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆