Home » Android » android – How to call Java API from NDK C++ thread?

android – How to call Java API from NDK C++ thread?

Posted by: admin May 14, 2020 Leave a comment

Questions:

I want to call Java API from NDK C++ thread, but env->FindClass() return 0. But when I call Java API in main thread, it works well. I’ve already call AttachCurrentThread() in the thread, can anyone help me?

Here is the source code:

JAVA CODE:

public class simple_test extends Activity {
    ...
    // This functin will be called in C++
    public void PrintNdkLog(String slog) {
        Log.e(logTagNDK, slog);
        return;
    }
}

C++ CODE:

static JavaVM* g_JavaVM = NULL;

jobject getInstance(JNIEnv *env, jclass obj_class)
{
    jmethodID  c_id = env->GetMethodID(obj_class, "<init>", "()V");
    jobject obj = env->NewObject(obj_class, c_id);
    return obj;
}

// JNI OnLoad
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
    g_JavaVM = jvm;
    return JNI_VERSION_1_6;
}

// Call JAVA API "PrintNdkLog" in this function
void PrintNdkLog(char *lpLog)
{
    if (g_JavaVM == NULL)
        return;

    JNIEnv *env = NULL;
    g_JavaVM->GetEnv((void**)&env, JNI_VERSION_1_6);
    if (env == NULL)
        return;

    jclass cls = env->FindClass("com/myndk/simple_test");
    if (cls != 0) // **cls will be 0 when PrintNdkLog() is called in thread**
    {
        LOGE("FindClass error %p", cls);
    }
    else
    {
        jmethodID mid;
        jobject obj;
        obj = getInstance(env, cls);
        mid = env->GetMethodID(cls, "PrintNdkLog", "(Ljava/lang/String;)V");
        if (mid != 0)
        {
            jstring jstrMSG = env->NewStringUTF(lpLog);
            env->CallVoidMethod(obj, mid, jstrMSG);
        }
    }
}

// Call JAVA API in thread
static void* thread_test(void* ptr)
{
    JNIEnv *envLocal;
    int status = g_JavaVM->GetEnv((void **) &envLocal, JNI_VERSION_1_6);
    if (status == JNI_EDETACHED)
    {
        status = g_JavaVM->AttachCurrentThread(&envLocal, NULL);
        if (status != JNI_OK)
            LOGE("AttachCurrentThread failed %d",status);
    }
    PrintNdkLog("bbb"); // This JAVA callback failed, and printed "FindClass error"
}

// Create thread
int NdkThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam, int nPriority)
{
    PrintNdkLog("aaa"); // This JAVA callback runs well
    pthread_t pid;
    pthread_create(&pid, NULL, thread_test, pParam);
}
How to&Answers:

I have solved it now.
In NDK native thread, only can call static Java API. If you call env->FindClass(), it would trigger an exception.
http://android.wooyd.org/JNIExample gived the detail info.

Answer:

Suggest to take a look on AttachCurrentThread.

Here is a sample code to do that:

// Global variable
JavaVM *g_jvm = NULL;  //Get g_jvm from jni main thread use env->GetJavaVM(&g_jvm);
jobject g_obj = NULL;  //Where the java function exist. (some activity)

//Get env in thread function and attach the env
JNIEnv *env;
if(g_jvm->AttachCurrentThread(&env, NULL) != JNI_OK)
{
    LOGD("%s: AttachCurrentThread() failed", __FUNCTION__);
}

const char * fnName ="somFunctionInYourJava"; //which should be "pulic void somFunctionInYourJava(String input);"
jstring retStr = env->NewStringUTF(str);
jclass cls = env->GetObjectClass(thiz);

jmethodID messageMe = env->GetMethodID(cls, fnName, "(Ljava/lang/String;)V");
env->CallVoidMethod(thiz, messageMe, retStr);

//Detach thread and release related resource    
if(g_jvm->DetachCurrentThread() != JNI_OK)
{
    LOGD("%s: DetachCurrentThread() failed", __FUNCTION__);
}