r17b r16 r13 ndk64 ndk android c++ jni

android - r16 - ndk r13



FindClass de cualquier hilo en Android JNI (2)

Después de mucho intentar y bloquear mi aplicación, un colega y yo logramos almacenar en caché y usar con éxito el cargador de clases en otro hilo nativo. El código que usamos se muestra a continuación (C ++ 11, pero se convirtió fácilmente a C ++ 2003), publicado aquí ya que no pudimos encontrar ningún ejemplo de la "memoria caché" mencionada anteriormente en algún lugar útil, y emitimos loadClass llama directamente. Esto requiere un poco de esfuerzo ". Llamar a findClass funcionó perfectamente cuando se lo llamó 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; }

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

Guarde en caché una referencia al objeto de ClassLoader en algún lugar útil y emita llamadas de loadClass directamente. Esto requiere un poco de esfuerzo.

Entonces, traté de hacerlo funcionar y parece que no importa qué, este método simplemente no funciona para mí. Eventualmente, calculé cómo usar ClassLoader pero no funcionará si desde un hilo nativo trato de cargarClass que aún no ha sido tocado / cargado. Esencialmente, es el comportamiento idéntico al de env-> FindClass cuando se llama desde un hilo nativo, con la excepción de que no devolverá 0 para las clases que ya se usaron en la aplicación. Alguna idea si no lo hice bien o si es imposible acceder a las clases desde un hilo nativo que aún no se hayan utilizado / cargado.






EDITAR: Daré más información para explicar qué es exactamente lo que quiero decir. Hay un JNI env->FindClass(className) regular, y otro que escribí myFindClass(env, className) que usa ClassLoader->loadClass caché.

La clase a la que estoy intentando acceder desde c / c ++ nativo es "com / noname / TestClient". Dentro de myFindClass también utilizo env-> FindClass y el 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 y c1 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 y c1 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 tercer caso. ¿Es un error? ¿Qué se puede hacer para solucionar el problema?

EDIT2: Además de eso, parece que ClassLoader :: loadClass es claramente defectuoso. Si le pregunto a myFindClass ("noname / TestClent"), devuelve algo de basura, y cuando uso ese jclass devuelto de alguna manera la aplicación falla.


Intente asociar su hilo nativo a la JVM primero.

El puntero a jvm puede obtener lo primero en JNI_OnLoad

env->GetJavaVM(&jvm);

Luego de tu hilo nativo

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

Entonces usa ese env para FindClass