FindClass de cualquier hilo en Android JNI

La página de consejos de JNI de Android menciona esta FAQ: ¿Por qué FindClass no encontró mi clase? Mencionan múltiples soluciones y la última opción es esta:

Cache una referencia al objeto ClassLoader en algún lugar práctico y emita las llamadas loadClass directamente. Esto requiere cierto esfuerzo.

Por lo tanto, traté de conseguir que funcione y parece que no importa qué, este método simplemente no funciona para mí. Eventualmente, pensé en cómo usar ClassLoader pero no funcionará si de un hilo nativo intento a loadClass que no ha sido tocado / cargado todavía. Esencialmente, es idéntico a env-> FindClass en comportamiento cuando se llama desde un subproceso nativo, con la excepción de que no devolverá 0 para las clases que ya se usan en la aplicación. Cualquier idea si no lo hice bien, o es imposible acceder a las clases de un hilo nativo que no fueron utilizados / cargado todavía.

EDIT: Voy a dar más información para explicar exactamente lo que quiero decir. Hay JNI normal env->FindClass(className) , y otro que escribí myFindClass(env, className) que utiliza en caché ClassLoader->loadClass .

La clase a la que intento acceder desde c / c ++ nativo es "com / noname / TestClient". Dentro de myFindClass también uso env-> FindClass y valor de registro que devuelve:

 jclass myFindClass(JNIEnv * env, const char* name) { ... jclass c0 = env->FindClass(name); jclass c1 = (jclass)env->CallObjectMethod(ClassLoader, MID_loadClass, envNewStringUTF(name)); dlog("myFindClass(\"%s\") => c0:%p, c1:%p, c0 and c1 are same: %d", name, c0, c1, env->IsSameObject(c0, c1)); ... } 

Entonces, tengo estas 3 combinaciones para explicar el problema.

1}

 //inside JNI_OnLoad thread myFindClass(env, "com/noname/TestClient"); ... //inside native thread created by pthread_create myFindClass(env, "com/noname/TestClient"); 

Obtengo este logcat:

MyFindClass ("com / noname / TestClent") => c0: 0x41b64558, c1: 0x41b64558, c0 y c1 son los mismos: 1

MyFindClass ("com / noname / TestClent") => c0: 0, c1: 0x41b64558, c0 yc1 son los mismos: 0

2)

 //inside JNI_OnLoad thread env->FindClass("com/noname/TestClient"); ... //inside native thread created by pthread_create myFindClass("com/noname/TestClient"); 

Obtengo este logcat:

MyFindClass ("com / noname / TestClent") => c0: 0, c1: 0x41b64558, c0 yc1 son los mismos: 0

3)

 //inside JNI_OnLoad thread //"com/noname/TestClient" isn't touched from JNI_OnLoad. ... //inside native thread created by pthread_create myFindClass(env, "com/noname/TestClient"); 

Obtengo este logcat:

MyFindClass ("com / noname / TestClent") => c0: 0, c1: 0, c0 y c1 son los mismos: 1

Básicamente, mi problema es que ClassLoader no encuentra mi clase en el 3er caso. ¿Es un error? ¿Qué se puede hacer para solucionar el problema?

EDIT2: Además de eso, parece que ClassLoader :: loadClass es claramente buggy. Si le pregunto a myFindClass ("noname / TestClent"), entonces devuelve algo de basura, y cuando utilizo esa jclass devuelta de alguna manera la aplicación se bloquea.

Después de mucho intentar y estrellarse de mi aplicación, un colega y yo conseguimos almacenar en caché y utilizar con éxito el cargador de clase en otro, hilo nativo. El código que utilizamos se muestra a continuación (C ++ 11, pero fácilmente convertido a C ++ 2003), publicado aquí, ya que no pudimos encontrar ningún ejemplo de la mencionada "Cache una referencia al objeto ClassLoader en algún lugar práctico, y el problema loadClass Esto requiere un esfuerzo. ". Llamar findClass funcionado perfectamente cuando se llama desde un hilo diferente al de JNI_OnLoad. Espero que esto ayude.

 JavaVM* gJvm = nullptr; static jobject gClassLoader; static jmethodID gFindClassMethod; JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *pjvm, void *reserved) { gJvm = pjvm; // cache the JavaVM pointer auto env = getEnv(); //replace with one of your classes in the line below auto randomClass = env->FindClass("com/example/RandomClass"); jclass classClass = env->GetObjectClass(randomClass); auto classLoaderClass = env->FindClass("java/lang/ClassLoader"); auto getClassLoaderMethod = env->GetMethodID(classClass, "getClassLoader", "()Ljava/lang/ClassLoader;"); gClassLoader = env->CallObjectMethod(randomClass, getClassLoaderMethod); gFindClassMethod = env->GetMethodID(classLoaderClass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;"); return JNI_VERSION_1_6; } jclass findClass(const char* name) { return static_cast<jclass>(getEnv()->CallObjectMethod(gClassLoader, gFindClassMethod, getEnv()->NewStringUTF(name))); } JNIEnv* getEnv() { JNIEnv *env; int status = gJvm->GetEnv((void**)&env, JNI_VERSION_1_6); if(status < 0) { status = gJvm->AttachCurrentThread(&env, NULL); if(status < 0) { return nullptr; } } return env; } 

Intente conectar primero su hilo nativo a la JVM.

El puntero a jvm se puede obtener lo primero en JNI_OnLoad

 env->GetJavaVM(&jvm); 

Luego, desde tu hilo nativo

 JNIEnv *env; jvm->AttachCurrentThread((void **)&env, NULL); 

A continuación, utilice ese env para FindClass

  • Llamar al miembro de la clase JAVA desde el código nativo C / C ++
  • Defragment H264 NAL corriente (originalmente 1722 avb paquetes)
  • Compilar Java para Android nativo en lugar de escribir en C
  • ¿Cómo utilizar Java con NDK Android?
  • Multiprocesamiento en Android
  • Enviar cadena C ++ a Java a través de JNI
  • Cómo especificar el archivo de encabezado c en el archivo android.mk
  • Android NDK: crea dos bibliotecas compartidas nativas que se llaman
  • Android JNI ¿Cuál es el directorio de trabajo actual para C / C ++ ejecutado código?
  • Escriba a stdout en JNI Android? Sin <android / log.h>
  • No se puede acceder a AAssetManager en código nativo pasado de Java en WallpaperService
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.