JNI FindClass() cannot find a class if it's called by a new pthread

JNI FindClass() cannot find a class if it's called by a new pthread
0.0 0


I’m struggling against an issue regarding Java method call from a newly created thread through JNI.

If I call ‘FindClass()’ for a Java class from the main thread, it gets a proper class reference.
But if it is called with same class identifier from a thread which is created using pthread, it returns NULL. Java exception is ‘NoClassDefFoundError’.
It looks like it’s because the class loader the thread uses is different with the application loaded.

I found some articles on internet which have similar issues. And seems like one way to do this is to keep a cache of the class’ global reference in ‘JNI_OnLoad()’. And it works well.
But it is just a workaround. I don’t want to keep the references of all Java classes in ‘JNI_OnLoad()’. I don’t even want to mention any Java class I wrote in ‘JNI_OnLoad()’.

Has anyone had this issue already?
Isn’t there a way to force them to use one class loader or am I missing something?


Could you give me your sample code about the workaround,
I don’t know how to implement it.
Thank you.


my email: jumpchess@163.com


The concept of the workaround is to get the IDs of Java classes which are used in all application domain at application startup time, and keep the Java class IDs until the application ends.

In the following code, getClassId() function loads a Java class and returns its class ID. During that, it keeps the class ID in a static map.
You just call getClassId() with the required Java classes in nativeInit() in main.cpp before CCApplication::sharedApplication()->run().
The Java class IDs are correctly retrieved without NoClassDefFoundError exception because this is the main thread.
Then the Java class IDs are being kept in the static map, and can be retrieved later and used by any other threads than main thread.

You need to keep in mind that you should add a Java class name in the map whenever you need to use a new Java class.

With this workaround, there have been no problem so far in our two live products.
Anyway, this is just workaround. And I want that NDK Java class loader problem be sorted out.

    // This map is to keep global references for already looked up classes. This reduces lookup time and soothes the 'different class loaders for threads' problem.
    static map  s_mClassGlobalReferences;

jclass NFJniManager::getClassId(const char *className, JNIEnv *env)
    jclass classId = NULL;
    string classFullyQualifiedPath(className);

    // TODO
    map::iterator it = s_mClassGlobalReferences.find(classFullyQualifiedPath);
    if(it != s_mClassGlobalReferences.end()) {
        classId = it->second;
    else {
        if (env == NULL)
            env = getJNIEnv();

        jclass classId = env->FindClass(className);
        if (classId == NULL) {
            NFLog(NF_ERR, "Failed to find class of '%s'", className);
            return classId;
        classId = static_cast(env->NewGlobalRef(classId));
        if (classId == NULL) {
            NFLog(NF_ERR, "Failed to get global reference of '%s'", className);
            return classId;

        s_mClassGlobalReferences[classFullyQualifiedPath] = classId;
        NFLog(NF_DEBUG, "Class ID for '%s' is loaded.", className);
    return classId;