simpx
simpx

Reputation: 184

JNI GetLongField failed with "jni not valid for an object of class java.lang.Class"

java code:

public class Foo {
    private long i = 0;
    void printI() {
        nativePrintI();
    }      
    private native static void nativePrintI();
}

JNI code:

JNIEXPORT void JNICALL Java_com_aliyun_livestream_Publisher_nativeRelease
    (JNIEnv *env, jobject obj) {
    jclass cls = env->GetObjectClass(obj);;
    jfieldID iField = env->GetFieldID(cls, "i", "J");
    jlong i = env->GetLongField(obj, iField);
    printf("%lld\n", i);
}

my callee like this:

Foo foo = new Foo();
foo.printI();

sometimes It works fine, but sometimes It failed with message ""jni not valid for an object of class java.lang.Class"

Upvotes: 1

Views: 2829

Answers (2)

Michael
Michael

Reputation: 58467

When you declare a native method as static, the corresponding JNI function will receive a JNIEnv* and a jclass, plus whatever additional arguments you have specified (in this case none). The jclass argument is a reference to the class on which the method was invoked.
This is really no different from calls you make within Java; when you call a static method it will be invoked on a class rather than an object instantiated from that class.

A non-static native method on the other hand will call the JNI function with a JNIEnv* and a jobject, where the jobject is a reference to the object on which the method was invoked.

In C/C++, a jobject and a jclass are both pointers, so you'll be able to assign one to the other, but when you try to pass or return them to a Java/JNI function it will matter what kind of underlying values those pointers point to.
GetObjectClass and GetLongField expect that you pass them a jobject, so the jclass that you get in the static case will not work.

If you want a jobject, declare the method as non-static in Java. If you want a jclass, declare the method as static in Java. If you want both, declare it non-static so that you receive a jobject which you then can call GetObjectClass on.

Upvotes: 6

simpx
simpx

Reputation: 184

use snippets from Can I know the name of the class that calls a JNI C method?

jclass cls = env->GetObjectClass(obj);

// First get the class object
jmethodID mid = env->GetMethodID(cls, "getClass", "()Ljava/lang/Class;");
jobject clsObj = env->CallObjectMethod(obj, mid);

// Now get the class object's class descriptor
cls = env->GetObjectClass(clsObj);

// Find the getName() method on the class object
mid = env->GetMethodID(cls, "getName", "()Ljava/lang/String;");

// Call the getName() to get a jstring object back
jstring strObj = (jstring)env->CallObjectMethod(clsObj, mid);

// Now get the c string from the java jstring object
const char* str = env->GetStringUTFChars(strObj, NULL);

// Print the class name
printf("\nCalling class is: %s\n", str);

// Release the memory pinned char array
env->ReleaseStringUTFChars(strObj, str);

It turns out, when the Calling class name is "java.lang.Class", GetLongField fails. yet, when the Calling class name is "Foo", GetLongField succeed.

Maybe it's due to Java's GC mechanism. Because the function nativePrintI is defined as static function. Java sometimes will call it without the valid Instance.

so change nativePrintI to private native void nativePrintI(); can solve the problems.

Upvotes: 1

Related Questions