在上几篇文章中,我们学习了jni的相关基础,这次我们就通过相关的jni函数操作来加强练习。
一、Native函数返回int数据
Java层定义的native方法。
二、Native层返回String类型数据。
Java层定义的native方法。
由于Java层和native层的String对象是无法直接进行交互的,所以我们需要进行数据类型的转换。在上面我们通过GetStringUTF方法进行转换成字符指针进行处理。这也是在jni中处理字符串的核心思路,转换成字符指针进行处理。下面是访问String的一些方法:
GetStringUTFChars将jstring转换成为UTF-8格式的char*
GetStringChars将jstring转换成为Unicode格式的char*
ReleaseStringUTFChars释放指向UTF-8格式的char*的指针
ReleaseStringChars释放指向Unicode格式的char*的指针
NewStringUTF创建一个UTF-8格式的String对象
NewString创建一个Unicode格式的String对象
GetStringUTFLengt获取 UTF-8格式的char*的长度
GetStringLength获取Unicode格式的char*的长度
三、Native层返回一维数组
Java层定义的native函数。
1)添加参数在(JNIEnv* env,jobject thiz) 后面添加 如:(JNIEnv* env,jobject thiz,jintArray nums )
2)获取数组的长度 jsize len =(*jniEnv)->GetArrayLength(jniEnv, nums);
3)新建数组 jintArray array =(*jniEnv)-> NewIntArray(jniEnv, len); 如果是新建别的数组,NewIntArray 要做相对应的改变
4)获取 数组里面的元素:
(*env)->GetIntArrayElements(env,nums, isCopy) , 返回 所有数据。If isCopy is not NULL, then *isCopy is set to JNI_TRUE if a copy ismade; if no copy is made, it is set to JNI_FALSE.
(*env)->GetIntArrayRegion(env,array,start,len,buffer), 从start开始复制长度为len 的数据到buffer中
5)设置 数组里面的元素
(*env)->SetIntArrayRegion(env,array,start,len,buffer) , 从start开始复制长度为len 的数据 buffer到 array 中
四、Native层返回二维数组
Java层native方法定义:
总结:
1.获取指定对象的类定义(jclass)
有两种途径来获取对象的类定义:第一种是在已知类名的情况下使用FindClass来查找对应的类。但是要注意类名并不同于平时写的Java代码,例如要得到类jni.test.Demo的定义必须调用如下代码:
jclass cls = (*env)->FindClass(env, "jni/test/Demo"); //把点号换成斜杠
然后通过对象直接得到其所对应的类定义:
jclass cls = (*env)-> GetObjectClass(env, obj);
//其中obj是要引用的对象,类型是jobject
2.读取要调用方法的定义(jmethodID)
我们先来看看JNI中获取方法定义的函数:
jmethodID (JNICALL *GetMethodID)(JNIEnv *env, jclass clazz, const char *name,
const char *sig);
jmethodID (JNICALL *GetStaticMethodID)(JNIEnv *env, jclass class, const char
*name, const char *sig);
这两个函数的区别在于GetStaticMethodID是用来获取静态方法的定义,GetMethodID则是获取非静态的方法定义。这两个函数都需要提供四个参数:env就是初始化虚拟机得到的JNI环境;第二个参数class是对象的类定义,也就是第一步得到的obj;第三个参数是方法名称;最重要的是第四个参数,这个参数是方法的定义。因为我们知道Java中允许方法的多态,仅仅是通过方法名并没有办法定位到一个具体的方法,因此需要第四个参数来指定方法的具体定义。但是怎么利用一个字符串来表示方法的具体定义呢?JDK中已经准备好一个反编译工具javap,通过这个工具就可以得到类中每个属性、方法的定义。
3.调用方法
为了调用对象的某个方法,可以使用函数CallMethod或者CallStaticMethod(访问类的静态方法),根据不同的返回类型而定。这些方法都是使用可变参数的定义,如果访问某个方法需要参数时,只需要把所有参数按照顺序填写到方法中就可以。
4.访问类属性
一、Native函数返回int数据
Java层定义的native方法。
public class HelloJni {
//求和
public native int getJniInt(int x, int y);
}
.c文件中的native方法实现。
JNIEXPORT jint JNICALL Java_com_dsw_hellojni_HelloJni_getJniInt
(JNIEnv *env, jobject obj, jint x, jint y){
return x + y;
}
针对基本数据类型,在java层和native层可以直接进行相互转换,无需进行过多的数据类型转换。
二、Native层返回String类型数据。
Java层定义的native方法。
public class HelloJni {
//String字符串倒序输出
public native String getJniString(String str);
}
native方法的实现:
JNIEXPORT jstring JNICALL Java_com_dsw_hellojni_HelloJni_getJniString
(JNIEnv *env, jobject obj, jstring str){
char *ch = (*env)->GetStringUTFChars(env,str,NULL);
char *endch = ch;
while(*endch){
endch++;
}
endch--;
while(endch >ch){
char t = *ch;
*ch = *endch;
*endch = t;
ch++;
endch--;
}
jstring resultStr = (*env)->NewStringUTF(env,ch);
return resultStr;
}
由于Java层和native层的String对象是无法直接进行交互的,所以我们需要进行数据类型的转换。在上面我们通过GetStringUTF方法进行转换成字符指针进行处理。这也是在jni中处理字符串的核心思路,转换成字符指针进行处理。下面是访问String的一些方法:
GetStringUTFChars将jstring转换成为UTF-8格式的char*
GetStringChars将jstring转换成为Unicode格式的char*
ReleaseStringUTFChars释放指向UTF-8格式的char*的指针
ReleaseStringChars释放指向Unicode格式的char*的指针
NewStringUTF创建一个UTF-8格式的String对象
NewString创建一个Unicode格式的String对象
GetStringUTFLengt获取 UTF-8格式的char*的长度
GetStringLength获取Unicode格式的char*的长度
三、Native层返回一维数组
Java层定义的native函数。
public class HelloJni {
public native int[] getJniArrayInt();
}
native方法的实现:
JNIEXPORT jintArray JNICALL Java_com_dsw_hellojni_HelloJni_getJniArrayInt
(JNIEnv *env, jobject obj){
jintArray intArray = (*env)->NewIntArray(env,20);
jint result[20];
jint i = 1;
for(i = 0; i<10;i++){
result[i] = i;
}
(*env)->SetIntArrayRegion(env,intArray,0,10,result);
return intArray;
}
这里注意,1)添加参数在(JNIEnv* env,jobject thiz) 后面添加 如:(JNIEnv* env,jobject thiz,jintArray nums )
2)获取数组的长度 jsize len =(*jniEnv)->GetArrayLength(jniEnv, nums);
3)新建数组 jintArray array =(*jniEnv)-> NewIntArray(jniEnv, len); 如果是新建别的数组,NewIntArray 要做相对应的改变
4)获取 数组里面的元素:
(*env)->GetIntArrayElements(env,nums, isCopy) , 返回 所有数据。If isCopy is not NULL, then *isCopy is set to JNI_TRUE if a copy ismade; if no copy is made, it is set to JNI_FALSE.
(*env)->GetIntArrayRegion(env,array,start,len,buffer), 从start开始复制长度为len 的数据到buffer中
5)设置 数组里面的元素
(*env)->SetIntArrayRegion(env,array,start,len,buffer) , 从start开始复制长度为len 的数据 buffer到 array 中
四、Native层返回二维数组
Java层native方法定义:
public class HelloJni {
public native int[] getJniArrayInt();
}
native方法的实现:
JNIEXPORT jobjectArray JNICALL Java_com_dsw_hellojni_HelloJni_getJniTwoArrayString
(JNIEnv *env, jobject obj){
jint dimion = 10;
jclass intArrayClass = env->FindClass("[I"); //获得一维数组 的类引用,即jintArray类型
//构造一个指向jintArray类一维数组的对象数组,该对象数组初始大小为dimion
jobjectArray obejctIntArray = env->NewObjectArray(dimion ,intArrayClass , NULL);
//构建dimion个一维数组,并且将其引用赋值给obejctIntArray对象数组
for( int i = 0 ; i< dimion ; i++ )
{
//构建jint型一维数组
jintArray intArray = env->NewIntArray(dimion);
jint temp[10] ; //初始化一个容器,用于装一行的值
for( int j = 0 ; j < dimion ; j++)
{
temp[j] = i + j ; //赋值
}
//将temp中的值赋值给intArray数组
env->SetIntArrayRegion(intArray, 0 , dimion ,temp);
//给object对象数组赋值,即保持对jint一维数组的引用
env->SetObjectArrayElement(obejctIntArray , i ,intArray);
env->DeleteLocalRef(intArray); //删除局部引用
}
return obejctIntArray; //返回该对象数组
}
总结:
1.获取指定对象的类定义(jclass)
有两种途径来获取对象的类定义:第一种是在已知类名的情况下使用FindClass来查找对应的类。但是要注意类名并不同于平时写的Java代码,例如要得到类jni.test.Demo的定义必须调用如下代码:
jclass cls = (*env)->FindClass(env, "jni/test/Demo"); //把点号换成斜杠
然后通过对象直接得到其所对应的类定义:
jclass cls = (*env)-> GetObjectClass(env, obj);
//其中obj是要引用的对象,类型是jobject
2.读取要调用方法的定义(jmethodID)
我们先来看看JNI中获取方法定义的函数:
jmethodID (JNICALL *GetMethodID)(JNIEnv *env, jclass clazz, const char *name,
const char *sig);
jmethodID (JNICALL *GetStaticMethodID)(JNIEnv *env, jclass class, const char
*name, const char *sig);
这两个函数的区别在于GetStaticMethodID是用来获取静态方法的定义,GetMethodID则是获取非静态的方法定义。这两个函数都需要提供四个参数:env就是初始化虚拟机得到的JNI环境;第二个参数class是对象的类定义,也就是第一步得到的obj;第三个参数是方法名称;最重要的是第四个参数,这个参数是方法的定义。因为我们知道Java中允许方法的多态,仅仅是通过方法名并没有办法定位到一个具体的方法,因此需要第四个参数来指定方法的具体定义。但是怎么利用一个字符串来表示方法的具体定义呢?JDK中已经准备好一个反编译工具javap,通过这个工具就可以得到类中每个属性、方法的定义。
3.调用方法
为了调用对象的某个方法,可以使用函数CallMethod或者CallStaticMethod(访问类的静态方法),根据不同的返回类型而定。这些方法都是使用可变参数的定义,如果访问某个方法需要参数时,只需要把所有参数按照顺序填写到方法中就可以。
4.访问类属性
访问类的属性与访问类的方法大体上是一致的,只不过是把方法变成属性而已。
更多文章参考:http://blog.csdn.net/qinjuning/article/details/7607214