引用型数据类型的操作:
jstring:
新建:
(C code-Unicode)
jstring javaString = (*env)->NewString(env, "hello world");(创建时可能返回NULL并报异常)
(C code-UTF8)
jstring javaString = (*env)->NewStringUTF(env, "hello world");(创建时可能返回NULL并报异常)
修改:无法修改。
类型转换 to C string:
(C code-Unicode)
jbyte* str = (*env)->GetStringChars(env, javaString, &isCopy);
(C code-UTF8)
jbyte* str = (*env)->GetStringUTFChars(env, javaString, &isCopy);
空间释放 for C string:
(C code-Unicode)
(*env)->ReleaseStringChars(env, javaString, str);
(C code-UTF8)
(*env)->ReleaseStringUTFChars(env, javaString, str);
数组:
新建:<Type> -> int , char, ...
(C code)
jintArray javaArray = (*env)->New<Type>Array(env, 10); (创建时可能返回NULL并报异常)
获取JAVA数组的拷贝并转换为C数组:GetIntArrayRegion
获取C数组的拷贝并转换为JAVA数组:SetIntArrayRegion
JAVA数组转换为C数组:GetIntArrayElements
C数组转换为JAVA数组:无,
释放转换后的C数组:Release<Type>ArrayElemens(忘记释放会造成内存泄露)
Byte Buffer:
新建:
unsigned char* buffer = (unsigned char*) malloc(1024);(注意回收,否者内存泄露)
jobject directBuffer;
directBuffer = (*env)->NewDirectByteBuffer(env, buffer, 1024);
Byte Buffer 类型转换为 ByteArray:
unsigned char* buffer;
buffer = (unsigned char*) (*env)->GetDirectBufferAddress(env,directBuffer);
类成员访问
1、获取Java Class 类
jclass clazz = (*env)->GetObjectClass(env, instance);
2、获取成员ID
对象成员
jfieldID instanceFieldId;
instanceFieldId = (*env)->GetFieldID(env, clazz,"instanceField", "Ljava/lang/String;");
静态成员
jfieldID staticFieldId;
staticFieldId = (*env)->GetStaticFieldID(env, clazz,"staticField", "Ljava/lang/String;");
注:
“Ljava/lang/String;" 成员的类型描述。(field type in Java)
3、获取成员:
对象成员:
静态成员:
jstring staticField;
staticField = (*env)->GetStaticObjectField(env, clazz, staticFieldId);(可能由于内存泄露,返回NULL)
TIP:由于获取成员变量需要2到3个JNI方法的调用,容易降低性能,所以强烈建议在上层把成员作为参数传给NATIVE函数。
方法调用:
1、获取Java Class 类
jclass clazz = (*env)->GetObjectClass(env, instance);
2、获取方法ID:
对象方法:
jmethodID instanceMethodId;
instanceMethodId = (*env)->GetMethodID(env, clazz, "instanceMethod", "()Ljava/lang/String;");
静态方法:
jmethodID staticMethodId;
staticMethodId = (*env)->GetStaticMethodID(env, clazz,"staticMethod", "()Ljava/lang/String;");
注:"()Ljava/lang/String;"方法的签名。(method signature in Java)
3、调用方法:
对象方法:
jstring instanceMethodResult;
instanceMethodResult = (*env)->CallStringMethod(env,instance, instanceMethodId);
静态方法:
CallStatic<Type>Field
Tip:Table3-4为java 类型的签名表
JAVA类反汇编:
JAVAP工具:
命令行调用:
Eclipse Extemal Tool调用:
异常处理:
抓取异常:
jthrowable ex;
(*env)->CallVoidMethod(env, instance, throwingMethodId);
ex = (*env)->ExceptionOccurred(env);
if (0 != ex) {
(*env)->ExceptionClear(env);
/* Exception handler. */
}
抛出异常:
jclass clazz;
...
clazz = (*env)->FindClass(env, "java/lang/NullPointerException");
if (0 ! = clazz) {
(*env)->ThrowNew(env, clazz, "Exception message.");
}
由于代码不是运行在java虚拟机内的,所以抛出异常不会终止代码的运行,所以在抛出异常的时候,应该在C层手动释放资源。
如果你的资源是引用过来的,会可以在从native函数返回后,自动被虚拟机回收
对象引用:
本地引用:大多数JNI方法都会返回局部引用
在native函数返回时回收。
删除本地引用:
(*env)->DeleteLocalRef(env, localClazz);
全局引用:可以在之后的所有native函数中使用,直到被释放。
新建:基于一个局部引用创建
jclass localClazz;
jclass globalClazz;
...
localClazz = (*env)->FindClass(env, "java/lang/String");
globalClazz = (*env)->NewGlobalRef(env, localClazz);
...
(*env)->DeleteLocalRef(env, localClazz);
删除全局引用:
(*env)->DeleteGlobalRef(env, globalClazz);
弱全局引用:同样可以在之后的所有native函数中使用。但是弱全局引用不能保证对象不被回收。
新建:
jclass weakGlobalClazz;
weakGlobalClazz = (*env)->NewWeakGlobalRef(env, localClazz);
查看引用的对象是否还存在:(很奇怪为什么不需要考虑线程安全)
if (JNI_FALSE == (*env)->IsSameObject(env, weakGlobalClazz, NULL)) {
/* Object is still live and can be used. */
} else {
/* Object is garbage collected and cannot be used. */
}
删除弱全局引用:
(*env)->DeleteWeakGlobalRef(env, weakGlobalClazz);
线程:
TIPS:
1、本地引用只能被一个线程的上下文调用。全剧以农可以被多个线程调用。
2、传递给某个native函数的JNIEnv指针也不能在别的线程中被调用。(开发者有可能在native函数中缓存JNIEnv指针,并在别的线程调用的native函数中使用这个JNIEnv指针。
线程同步块:
JAVA:
synchronized(obj) {
/* Synchronized thread-safe code block. */
}
NATIVE method:
if (JNI_OK == (*env)->MonitorEnter(env, obj)) {
/* Error handling. */
}
/* Synchronized thread-safe code block. */
if (JNI_OK == (*env)->MonitorExit(env, obj)) {
/* Error handling. */
}
TIPS:
1、为了预防死锁,一定要成对调用MonitorEnver和MonitorExit方法;
NATIVE线程:
AttachingAnd Detaching:
JavaVM* cachedJvm;
...
JNIEnv* env;
...
/* Attach the current thread to virtual machine. */
(*cachedJvm)->AttachCurrentThread(cachedJvm, &env, NULL);
/* Thread can communicate with the Java application
using the JNIEnv interface. */
/* Detach the current thread from virtual machine. */
(*cachedJvm)->DetachCurrentThread(cachedJvm);
由于之前提到,一个JNIEnv指针不能在不同线程中使用,所以NATIVE线程需要从java虚拟机得到新的JNIEnv指针。
更多JNI接口方法和相关介绍见:
http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/jniTOC.html