《Pro Android C++ with the NDK》 第三章阅读笔记



引用型数据类型的操作:

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



















你可能感兴趣的:(android,jni,NDK)