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.
- ¿Cómo puedo cargar mi propia clase Java en C en Android?
- java.lang.UnsatisfiedLinkError: Método nativo no encontrado
- Integración o compilación de instrucciones para libjpeg-turbo en Android
- No se puede resolver el símbolo webrtc en Android Studio
- ¿Puedo usar excepciones de C ++ en la biblioteca JNI en Android?
- Implementar MIDI con Delphi en Android
- JNI ERROR DETECTADO EN APLICACIÓN: entrada no es válida UTF-8 modificado: byte de inicio ilegal 0xfc.
- Decodificación y manipulación de imágenes mediante JNI en android
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
- Android: Implementación de la barra de progreso y "cargando …" para Lista sin fin como Android Market
- La consola de Eclipse muestra: 'Error al pulsar la selección: sistema de archivos de sólo lectura' cuando intento empujar un archivo