Я пытался создать обратный вызов из jni в java, но когда когда-либо выполняю свой код из потока cpp, метод findClass возвращает ноль, но тот же путь к классу & метод, правильно выполняющийся из потока jni (Native Thread).
static JavaVM * s_Jvm;JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
s_Jvm = vm;
return JNI_VERSION_1_4;
}void callbackToJava() {
JNIEnv *env;
res = s_Jvm->GetEnv((void**) &env, JNI_VERSION_1_4);
LOGI("status:%d",res);
if(res < 0) {
LOGI("callback_handler: failed to get JNI environment, ""assuming native thread");
res = s_Jvm->AttachCurrentThreadAsDaemon(&env, NULL);
LOGI("res:%d",res);
if(res == JNI_OK) {
LOGI("JNI_ok");
}
if(res < 0) {
LOGI("callback_handler: failed to attach ""current thread");
return;
}
}
if (res < 0) {
LOGI("Can't create Java VM\n");
return;
}
cls = (env)->FindClass("com/test/controller/NativeTest");
if (cls == NULL) {
LOGI("Class is null"); //**Error Line**
goto destroy;
}
mid = (env)->GetStaticMethodID(cls, "display",
"()V");
if (mid == NULL) {
LOGI("Mid is null");
goto destroy;
}
(env)->CallStaticVoidMethod(cls, mid, "()V");
destroy:
if ((env)->ExceptionOccurred()) {
(env)->ExceptionDescribe();
}
(jvm)->DestroyJavaVM();
}
Я предпочитаю создавать функцию в потоке jni и вызывать ее из других потоков кода cpp. Смотрите пример проекта https://github.com/NickZt/MyJNACallbackTest
Сохранить каждого слушателя в динамический массив
env-> GetJavaVM (&JVM); // сохранить ссылку на jvm для последующего вызова
store_env = env;
jweak store_Wlistener = env->NewWeakGlobalRef(listener);
jclass clazz = env->GetObjectClass(store_Wlistener);
jmethodID store_method = env->GetMethodID(clazz, "onAcceptMessage", "(Ljava/lang/String;)V");
jmethodID store_methodVAL = env->GetMethodID(clazz, "onAcceptMessageVal", "(I)V");
ObserverChain *tmpt = new ObserverChain(store_Wlistener, store_method, store_methodVAL);
store_Wlistener_vector.push_back(tmpt);
обратный вызов сохраненных слушателей из кода C ++ с функцией test_string_callback_fom_c (char * val)
void test_string_callback_fom_c(char *val) {
__android_log_print(ANDROID_LOG_VERBOSE, "GetEnv:", " start Callback to JNL [%d] \n", val);
JNIEnv *g_env;
if (NULL == jvm) {
__android_log_print(ANDROID_LOG_ERROR, "GetEnv:", " No VM \n");
return;
}
// double check it's all ok
JavaVMAttachArgs args;
args.version = JNI_VERSION_1_6; // set your JNI version
args.name = NULL; // you might want to give the java thread a name
args.group = NULL; // you might want to assign the java thread to a ThreadGroup
int getEnvStat = jvm->GetEnv((void **) &g_env, JNI_VERSION_1_6);
if (getEnvStat == JNI_EDETACHED) {
__android_log_print(ANDROID_LOG_ERROR, "GetEnv:", " not attached\n");
if (jvm->AttachCurrentThread(&g_env, &args) != 0) {
__android_log_print(ANDROID_LOG_ERROR, "GetEnv:", " Failed to attach\n");
}
} else if (getEnvStat == JNI_OK) {
__android_log_print(ANDROID_LOG_VERBOSE, "GetEnv:", " JNI_OK\n");
} else if (getEnvStat == JNI_EVERSION) {
__android_log_print(ANDROID_LOG_ERROR, "GetEnv:", " version not supported\n");
}
jstring message = g_env->NewStringUTF(val);//
txtCallback(g_env, message);
if (g_env->ExceptionCheck()) {
g_env->ExceptionDescribe();
}
if (getEnvStat == JNI_EDETACHED) {
jvm->DetachCurrentThread();
}
}
extern "C"JNIEXPORT void JNICALL
Java_ua_zt_mezon_myjnacallbacktest_MainActivity_nonNextListener(JNIEnv *env, jobject instance,
jstring message_) {txtCallback(env, message_);
}
void txtCallback(JNIEnv *env, const _jstring *message_) {
if (!store_Wlistener_vector.empty()) {
for (int i = 0; i < store_Wlistener_vector.size(); i++) {
env->CallVoidMethod(store_Wlistener_vector[i]->store_Wlistener,
store_Wlistener_vector[i]->store_method, message_);
}
или из кода Java отправьте сообщение слушателям с помощью метода nonNextListener
Полное объяснение в https://dou.ua/lenta/articles/android-jni-callbacks