Reputation: 184
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
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
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