Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sharing a JavaVM* across threads in Android NDK

I want to call Java class methods from a cpp file that receives call backs from another executable.

To achieve this, I have retrieved a JavaVM pointer using the android::AndroidRuntime::getJavaVM() method in the .cpp file that directly receives JNI method calls. I am sharing this JavaVM pointer via the constructor to the eventual .cpp file where I call required Java methods as follows:

/* All the required objects(JNIEnv*,jclass,jmethodID,etc) are appropriately declared. */
**JNIEnv* env;
jvm->AttachCurrentThread(&env, NULL);
clazz = env->FindClass("com/skype/ref/NativeCodeCaller");
readFromAudioRecord = env->GetStaticMethodID(clazz, "readFromAudioRecord", "([B)I");
writeToAudioTrack = env->GetStaticMethodID(clazz, "writeToAudioTrack", "([B)I");** 

However, I get a SIGSEGV fault running this code.

According to the JNI documentation this seems to be the appropriate way to obtain JNIEnv in arbitary contexts: http://java.sun.com/docs/books/jni/html/other.html#26206

Any help in this regard will be appreciated.

Regards, Neeraj

like image 600
Neeraj Avatar asked Nov 04 '22 01:11

Neeraj


1 Answers

Global references will NOT prevent a segmentation fault in a new thread if you try to use a JNIEnv or JavaVM reference without attaching the thread to the VM. You were doing it properly the first time around, Mārtiņš Možeiko is mistaken in implying that there was something wrong with what you were doing.

Don't remove it, just learn how to use it. That guy doesn't know what he's talking about, if it's in jni.h you can be pretty sure it's not going anywhere. The reason it's not documented is probably because it's ridiculously self explanatory. You don't need to create GlobalReference objects or anything either, just do something like this:

#include <jni.h>
#include <string.h>
#include <stdio.h>
#include <android/log.h>
#include <linux/threads.h>
#include <pthread.h>

#define  LOG_TAG    "[NDK]"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define  LOGW(...)  __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

static pthread_mutex_t thread_mutex;
static pthread_t thread;
static JNIEnv* jniENV;

void *threadLoop()
{
    int exiting;
    JavaVM* jvm;
    int gotVM = (*jniENV)->GetJavaVM(jniENV,&jvm);
    LOGI("Got JVM: %s", (gotVM ? "false" : "true") );
    jclass javaClass;
    jmethodID javaMethodId;
    int attached = (*jvm)->AttachCurrentThread(jvm, &jniENV,NULL);
    if(attached>0)
    {
        LOGE("Failed to attach thread to JavaVM");
        exiting = 1;
    }
    else{
        javaClass= (*jniENV)->FindClass(jniENV, "com/justinbuser/nativecore/NativeThread");
        javaMethodId= (*jniENV)->GetStaticMethodID(jniENV, javaClass, "javaMethodName", "()V");
    }
    while(!exiting)
    {
        pthread_mutex_lock(&thread_mutex);
        (*jniENV)->CallStaticVoidMethod(jniENV, javaClass, javaMethodId);
        pthread_mutex_unlock(&thread_mutex);
    }
    LOGE("Thread Loop Exiting");
    void* retval;
    pthread_exit(retval);
    return retval;
}

void start_thread(){
    if(thread < 1)
        {
            if(pthread_mutex_init(&thread_mutex, NULL) != 0)
            {
                LOGE( "Error initing mutex" );
            }
            if(pthread_create(&thread, NULL, threadLoop, NULL) == 0)
            {
                LOGI( "Started thread#: %d", thread);
                if(pthread_detach(thread)!=0)
                {
                    LOGE( "Error detaching thread" );
                }
            }
            else
            {
                LOGE( "Error starting thread" );
            }
        }
}

JNIEXPORT void JNICALL
Java_com_justinbuser_nativecore_NativeMethods_startThread(JNIEnv * env, jobject this){
    jniENV = env;
    start_thread();
}
like image 170
Justin Buser Avatar answered Nov 15 '22 17:11

Justin Buser