JNI学习记录——基础操作与传递数据

最近在做的项目考虑调用WindowsAPI实现对程序内存的监控

而调用的接口是C/C++的 所以去学习了JNI的相关知识与基本的操作内容 最终还是很艰难的实现了想要实现的功能

把学习JNI的过程大致记录了下来 包含初步的基础操作与相互传递数据的操作

没有去深入学习原理  仅仅是“实现”了需要的功能 不足之处欢迎指正~

 

第一步:编写一个测试类

package com.tzy.test;

public class JNIDemo1 
{
	public native void JNITest1();

	public static void main(String[] args)
	{
		System.loadLibrary("TestJNI1");
		JNIDemo1 jniDemo1=new JNIDemo1();
		jniDemo1.JNITest1();
	}
}

这段代码中,JNITest1 为在Java程序中声明要在C/C++代码中实现的函数

TestJNI1则为最终与本Java程序实现连接的.c/.cpp名 之后在windows下会生成.dll后缀的同名文件,需要将这个文件路径导入到path中,在之后的步骤会详细说明

 

第二步:使用javah指令生成头文件

在该Java项目的bin目录下使用以下指令:

javah -classpath . -jni com.tzy.test.JNIDemo1

将生成一个头文件:

 

第三步:编写C/C++代码

我使用的是Visual Studio2015 编写的是C++代码

项目名称与Java代码中loadLibrary中的名字对应

勾选应用程序类型为DLL 附加选项空项目

项目建立完成在源文件中添加类:

 

接下来将

1.第二步中生成的头文件

2.JDK/include下的jni.h

3.JDK/include/win32下的jni_md.h

拷贝至C++项目的工程目录下

VS生成的工程目录TestJNI1中还有一个TestJNI1 拷贝至里面的那个

 

拷贝完成后在头文件中添加现有项

选中刚才拷贝的三个头文件

打开第二步中生成的头文件 把报错的#include<jni.h>改为#include "jni.h" 表示引用外部头文件

 

接下来编写TestJNI.cpp,将刚才修改的头文件中的

实现即可

#include "TestJNI.h"
#include "com_tzy_test_JNIDemo1.h"
#include <iostream>
using namespace std;

JNIEXPORT void JNICALL Java_com_tzy_test_JNIDemo1_JNITest1
(JNIEnv *, jobject)
{
	cout << "Hello World" << endl;
}

接着在解决方案上右键 ——属性

按以下方式修改

接着在项目上右键——生成

生成信息中包含目录,

复制该目录路径,在.dll上层

然后在Java项目中bulid path——Libraries——Native library location中添加

运行Java程序

成功输出

 

 

接下来进行传递数据的相应代码改变

其他部分操作不变,仅改变相应的Java与C++代码

首先进行一个简单的值的传递示范

package com.tzy.test;

public class JNIDemo1 
{
	public native int JNITest2(int chuanruzhi);

	public static void main(String[] args)
	{
		System.loadLibrary("TestJNI2");
		JNIDemo1 jniDemo1=new JNIDemo1();
		int a=jniDemo1.JNITest2(10);
		System.out.println(a);
	}
}

在这段代码中,把native函数返回值从void改为int 同时传入一个int至C++

然后主函数使用这个函数,传入C++的值为10 然后输出从C++传回的值

在javah生成的头文件中可以看到

对比刚才的函数

发现1.返回值由void变为jint 参数列表中多了一个jint 这个就是Java参数中的int

两者没有实际区别

同样对于boolean byte char short long float double 在C++中加上j就可以了

不同的在于字符串与数组的传递较为复杂

 

接下来在TestJNI2.cpp中实现这个函数

#include "TestJNI2.h"
#include "com_tzy_test_JNIDemo1.h"
#include<iostream>


JNIEXPORT jint JNICALL Java_com_tzy_test_JNIDemo1_JNITest2
(JNIEnv *, jobject, jint input)
{
	int output = input + 1;
	return output;
}

这段代码接收Java传入的int 并把这个值加1之后返回给Java

然后同样生成dll文件 在java项目中添加dll文件的上层目录

运行 

结果如下:

 

最后写一个字符串传递的例子

package com.tzy.test;

public class JNIDemo1 
{
	public native String JNITest3(String chuanruzhi);

	public static void main(String[] args)
	{
		System.loadLibrary("TestJNI3");
		JNIDemo1 jniDemo1=new JNIDemo1();
		String a=jniDemo1.JNITest3("Hello World from Java");
		System.out.println(a);
	}
}

在这段代码中,native函数返回值改为String 同时传入一个String给C++

javah生成的头文件中 函数为如下:

可以看到string变为了jstring 但是这个jstring与jint不同 jint可以直接当做C++中的int使用 而jstring却不能直接当做C++中的string使用

在cpp中进行以下处理:

#include "TestJNI3.h"
#include "com_tzy_test_JNIDemo1.h"
#include<iostream>


JNIEXPORT jstring JNICALL Java_com_tzy_test_JNIDemo1_JNITest3
(JNIEnv *env, jobject, jstring input)
{
	const char* str;

	str = env->GetStringUTFChars(input, false);

	std::cout << str << std::endl;

	env->ReleaseStringUTFChars(input, str);

	char* tmpstr = "Hello World from C++";

	jstring output= env->NewStringUTF(tmpstr);

	return output;
}

利用etStringUTFChars和NewStringUTF函数进行转换

这两个函数都是JNI API提供的函数

这样实现的功能是 利用C++输出Java传入的fromJava

再返回fromC++到Java中

生成 保存path

运行:

其他:

对于数组,会转换成 j<类型>Array,比如int[]对应的就是jintArray 利用JNI提供的GetIntArrayElements函数进行操作

具体请参照API

若想一次返回多个类型的值,可以使用Hashmap进行传递,object与jobject之间进行传递

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章