JNI入门值得注意的一些小东西(C语言)
一、java调用C代码步骤
第一步:编写java代码
写一个Java类,在成员位置声明需要用到的C程序的函数
public class TestInt {
public native int testInt(int num1,int num2);
}
上述代码就声明了一个名为testInt的本地函数,这个函数的实现将在C程序中实现。
第二步:编译java文件生成class文件,并由此生成.h文件
在命令行中在TestInt.java文件所在地用javac TestInt.java
命令,生成TestInt.class文件,或者用eclipse等java编辑器将其编译也行,反正就是要得到TestInt.class文件。
然后在该目录下用javah TestInt命令生成TestInt.h文件,TestInt.h文件便是我们编写C代码的前提。
第三步:编写C代码并生成可被java调用的 dll文件(利用vs 2010)
1、打开TestInt.h,可以看到其中已经生成了一部分代码
这个函数就是从TestInt.class中得到的,下面就按照这个将testInt函数在C程序中实现即可。
2、在vs2010中新建项目,选择Win32项目,然后给项目取一个名字(这个名字和最后生成的dll文件的名字一致)
依次选择dll和空项目
然后点击完成。
接下来将刚刚生成的TestInt.h文件复制到这个项目目录下
如图,放到TestInt文件夹(即项目目录)中
并去JDK文件夹中的include文件夹内的jni.h文件和include//win32文件夹内的jni_md.h文件也复制到项目目录中。
然后去vs中将那些头文件添加到当前工程中
添加完成后,将TestInt.h的函数声明中的#include<jni.h>修改为#include “jni.h”,可消除错误。然后新建一个类,用于编写C代码
在产生的.cpp文件中编写c代码
将刚刚TestInt.h中的函数部分直接复制过来,写上参数和函数体,在函数体类编写实现代码
JNIEXPORT jint JNICALL Java_TestInt_testInt
(JNIEnv *env, jobject obj ,jint num1,jint num2) {
int sum = 0;
sum = num1 + num2;
return sum;
}
然后右键点击解决方案->“属性”,点配置管理器,将配置信息改成如下图所示
然后右键点击项目->“生成”,即可在项目目录中看到TestInt.dll文件
然后在java代码中用System.loadlibrary("TestInt");
并在编辑器中将dll文件添加到库中,即可调用testInt方法。
[注] 如果在java代码中声明的native函数是非静态方法,那么在TestInt.h中产生的函数的第二个参数是jobject,这意味着这个函数到时候调用时是用对象调用,若在java代码中声明的函数是静态方法,那么在TestInt.h中产生的函数的第二个参数则是jclass,这意味着该函数到时候将是用类名直接调用。
二、数组参数传递时值得注意的一些小东西
1、函数中的参数为数组时要注意参数传递
比如说将Java中的一个int类型的数组传进来的时候,它的类型是jintArray
必须通过env参数将其用C语言中的数组进行处理
如:传进来的数组是jintArray arr,则应该建立一个指针来指向它
jint *arrPointer = env->GetIntArrayElements(arr,0);
这样就能操作arrPointer从而操作数组元素
不过最后必须释放内存才能将修改后的数组返回给jintArray arr
env->ReleaseIntArrayElements(arr,arrPointer,0);
//以上是释放内存的语句。用于取消C代码和java代码中数组数据的同步。
2、C中生成一个数组返回给java
可参考以下代码
JNIEXPORT jintArray JNICALL Test_jnitest_JniTest_outputArray
(JNIEnv *env, jobject jobj, jint len) {
// 创建一个jintArray数组变量
jintArray jint_Arr = (*env)->NewIntArray(env, len);
// 将jintArray转换成c的jint*指针进行数组赋值
jint *elements = (*env)->GetIntArrayElements(env, jint_Arr, NULL);
int i = 0;
for (; i < len; i++) {
elements[i] = i;
}
// 将C中的创建修改的数组同步到Java中
(*env)->ReleaseIntArrayElements(env, jint_Arr, elements, 0);
return jint_Arr;
}
3、如何处理java中传进来的二维数组
java代码如下:
public class Test {
public native int getArrayElement(int[][] arr);
public static void main(String [] args){
System.loadLibrary("ObjectArrayTest");
Test test = new Test();
int[][] array = new int[][]{{1,2,3},{4,5,6}};
int num = test.getArrayElement(array);
System.out.println(num);
}
C语言代码如下:
#include "Taste.h"
#include "jni.h"
#include "Test.h"
/*
* Class: Test
* Method: getArrayElement
* Signature: ([[I)I
*/
JNIEXPORT jint JNICALL Java_Test_getArrayElement
(JNIEnv *env, jobject obj, jobjectArray arr)
{
//获取二维数组的长度
int len = env->GetArrayLength(arr);
printf("len is%d\n",len);
//获取二维数组中存的第一个一维数组
jintArray arr1 = (jintArray)env->GetObjectArrayElement(arr,0);
//获取一维数组的长度
int len2 = env->GetArrayLength(arr1);
printf("len2 is%d\n",len2);
jint *arr1Pointer = env->GetIntArrayElements(arr1,0);
//通过jint类型的指针引用该一维数组,并打印该数组中的元素
printf("the first element in arr1 is %d\n",arr1Pointer[0]);
printf("the second element in arr1 is %d\n",arr1Pointer[1]);
printf("the third element in arr1 is %d\n",arr1Pointer[2]);
//获取二维数组中存放的第二个一维数组
jintArray arr2 = (jintArray)env->GetObjectArrayElement(arr,1);
jint *arr2Pointer = env->GetIntArrayElements(arr2,0);
//打印该数组的元素
printf("the first element in arr2 is %d\n",arr2Pointer[0]);
printf("the second element in arr2 is %d\n",arr2Pointer[1]);
printf("the third element in arr2 is %d\n",arr2Pointer[2]);
return arr1Pointer[0];
}
运行得到的结果为:
1
len is2
len2 is3
the first element in arr1 is 1
the second element in arr1 is 2
the third element in arr1 is 3
the first element in arr2 is 4
the second element in arr2 is 5
the third element in arr2 is 6
但是以上只是读取了Java那个二维数组的元素,并没有真正改变那个二维数组,要想真正改变那个数组中的元素,必须release语句实现。
我在C代码中添加以下语句:
//将第一个元素改变为10
arr1Pointer[0] = 10;
printf("the first element in arr1 is %d\n",arr1Pointer[0]);
上条打印语句的打印结果为:
the first element in arr1 is 10
说明此时arr1Pointer[0]的值确实已经改变了,但是并不知道Java中那个数组到底改变没有。
在Java代码中添加打印语句,检查上述C代码是否改变了Java中原有数组的元素值。
System.out.println("the first element in array is :"+array[0][0]);
打印结果为:
the first element in array is :1
因此单纯改变arr1Pointer并不能改变Java中二维数组array的元素值。只是获取了它的元素值。
在C代码中添加如下代码后成功改变了Java中那个二维数组的元素值
env->ReleaseIntArrayElements(arr1,arr1Pointer, 0);
再次打印的结果为:
the first element in array is :10