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

 2023-01-20    251  

问题描述

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

FindClass,GetMethodid,newObject,newGlobalRef

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

那些人会在跨线程中保持有效,还是我每次都必须获得新线程?如果后者,是否有一种方法可以在一个本机线程中创建对象并能够在另一个线程中访问相同的对象?

推荐答案

JNI方法,例如FindClass,GetMethodID,GetFieldID是昂贵的操作,可以保证在JVM的使用寿命中生成相同的结果.由于这些操作很耗时,因此明智的做法是将结果存储在本机一侧以后要重复使用的地方(这是 caching ).

JNI缓存仅考虑这些JNI函数调用.如果要缓存任何其他C ++或Java对象,这是另一个主题. (要清楚).

缓存的类,方法和字段不取决于它们从它们中检索的线程,因此它们在不同的线程上都是有效的.最多您必须使用Set<type>Field或Get<type>Field.

由于FindClass返回对 class 对象的本地引用,因此您必须将其转换为全局引用,以确保其在检索函数结束的函数后的重复使用.您可以使用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
  }
}

很有趣;)

其他推荐答案

对象不是线程特定的.它们最初是”本地”引用,如果您想保留副本,则必须告诉VM您正在通过创建(并最终删除)”全局”参考.

参见 -jni.html ,尤其是”本地和全局参考”部分.

以上所述是小编给大家介绍的缓存JNI对象和线程安全(在Android中),希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对77isp云服务器技术网的支持!

原文链接:https://77isp.com/post/26202.html

=========================================

https://77isp.com/ 为 “云服务器技术网” 唯一官方服务平台,请勿相信其他任何渠道。