有时候我写了个Java层的方法,希望native层也能够调用(尤其是一个实体类的get,set方法在native一般都会用到)。这在jni开发中也很常见,jni.h中也提供了很多方法。下面利用具体实例进行说明。这里直接使用AS3.0里面的CMake进行编译了,之后会讲解下Android.mk和Application.mk的用法和含义。这里我主要介绍一下几个:
1. java向native传递常用基本数据类型和字符串类型
2. java向native传递数组类型
3. java向native传递自定义java对象
4. java向native传递List对象
5. native向java返回字符串类型
6. native向java返回java对象
7. native向java返回数组类型
8. native向Java返回List对象
对于上面的每个都给出对应的例子。
本节所有案例代码均已放到GitHub上,欢迎下载:
<a href="https://github.com/huangtianyu/JNILearnCourse">https://github.com/huangtianyu/JNILearnCourse</a>
在我们调用方法时会用到方法的签名,使用类变量时需要用该变量对应的jni类型。下面给出对应的类型对照表。
1. 基本数据类型对照表:
2. 对象类型对照表:
3. 简写对应表
强大的AS在你写了java的native方法后,直接快捷键按Alt+Enter后即可生成对应的方法。
java层的方法:
生成后的native方法:
在上面可以看到,Java层的基本类型方法都会经过jni进行转换,转换成相应的jni类型。其操作也很方便。Java的String类型需要注意下,一般是将jstring先转换为char*然后对char *进行操作。由于这获取了一个局部引用,一般在调用结束后需要释放该局部引用。
对应的jni方法是:
其中函数: jsize GetArrayLength(jxxxarray array);用于获取数组的长度
在Java端调用代码如下:
(1) GetXXXArrayElements(Array arr , jboolean* isCopide);
这类函数可以把Java基本类型的数组转换到C/C++中的数组,有两种处理方式,一种JNI_TRUE是拷贝一份传回本地代码,另一个是JNI_FALSE把指向Java数组的指针直接传回到本地代码中,处理完本地化的数组后,通过ReleaseXXXArrayElements来释放数组
(2) ReleaseXXXArrayElements(Array arr , * array , jint mode)
用这个函数可以选择将如何处理Java跟C++的数组,是提交,还是撤销等,内存释放还是不释放等mode可以取下面的值:
0 :对Java的数组进行更新并释放C/C++的数组
JNI_COMMIT :对Java的数组进行更新但是不释放C/C++的数组
JNI_ABORT:对Java的数组不进行更新,释放C/C++的数组
(3) GetPrimitiveArrayCritical(jarray arr , jboolean* isCopied);
在获得数组上的锁后将返回一个句柄给数组。如果没有建立任何锁,则isCopy被置为JNI_TRUE,否则置为NULL或JNI_FALSE:
(4) ReleasePrimitiveArrayCritical(jarray arr , void* array , jint mode);
释放从GetPrimitiveArrayCritical调用中返回的数组。也是JDK1.2出来的,为了增加直接传回指向Java数组的指针而加入的函数,同样的也会有同GetStringCritical的死锁的问题。mode取值如下:
0:从carray中复制值到数组中,并释放分配给carray的存储器
JNI_COMMIT:从carray中复制值到数组中,但是不释放分配给carray的存储器
JNI_ABORT:不从carray中复制值到数组中
(5) GetXXXArrayRegion(Array arr , jsize start , jsize len , * buffer);
在C/C++预先开辟一段内存,然后把Java基本类型的数组拷贝到这段内存中。用于一个数组子集的复制操作。参数start指定了从何处复制的起始索引,参数len则指定了从数组中复制到本机数组的多个位置数量。
(6) SetXXXArrayRegion(Array arr , jsize start , jsize len , const * buffer);
用来复制本机数组的一段内容回Java数组中。元素一般从本机数组起始处(索引为0)开始复制,但是只是从位置start开始将len个元素复制到Java数组中。
(7) Array NewXXXArray(jsize sz)
创建一个包含length个元素的Java数组。
(1) jobjectArray NewObjectArray(jsize length, jclass elementClass, jobject initialElement );
创建对象数组,创建一个长度为length,并且持有类型为elementClass的对象的对象数组,数组中的所有元素都被置为initialElement
(2) jobject GetObjectArrayElement(jobjectArray array, jsize Index);
获取数组元素,通过Index指定的索引在array中获取一个对象,如果索引超出边界,会抛出一个IndexOutOfBoundsException
(3) void SetObjectArrayElement(jobjectArray array, jsize index,jobject value);
设置元素值。在array中通过index指定的索引处设置元素值为value,如果index超出边界,会抛出一个IndexOutOfBoundException。
定义一个Java层方法:
定义一个Java的native方法:
在native层实现
在Java端调用
从Java端传对象实例给native时,到native端任何对象都变为jobject类型,如果要做对该对象实例的任何操作需先获取该对象的jfieldID ,jmethodID,然后通过env->CallXXXMethod来操作该对象的方法其中第一个参数是该对象的具体实例,其中env->CallStaticXXXMethod方法用来调用该类的静态方法,调用静态方法的时候就不用传具体的对象过去了。
定义Java的native方法
在native中实现具体方法:
<code>jclass cls = env->GetObjectClass(people);</code>这是获取一个对象实例相应的类的最好的办法。从上面可以看出List在传到native时也是变成了jobject,然后具体操作都得通过env->GetObjectClass先获取到该类,然后获取到该类的具体jmethodID,jfieldID来完成相应的操作。调用的方法也是env->CallXXXMethod()。
然后在Java端调用该native方法:
在上面即可通过native将Person的name全部进行了更改。
<b>上面</b>都是Java向native传参,基本用法都类似。基本数据类型有相应的对照表,对象类型的都转为jobject,对对象的操作都是先获取该对象jclass,jmethodID,jfielID后再对对象实例进行操作。
在JNI里面的方法有很多,记起来也较为麻烦。当有不会用时,可以参考以下手册,里面翻译了JNI常用的方法。
<a href="http://www.ceeger.com/Script/AndroidJNI/AndroidJNI.html">http://www.ceeger.com/Script/AndroidJNI/AndroidJNI.html</a>