前言
编程语言多种多样,在一个工程中有时会用到多种编程语言,这时就需要一个方法将两种语言联系起来,下文小编以 jni 为例,jni 用于在 JAVA 程序中引入 C 或 C++ 文件。
具体方法
- 首先在本地任意的一个目录下创建2各类 JNI.java 和 Test.java ,我在 D:\JNITest 目录下创建了这两个文件。
//JNI.java文件
public class JNI{
//创建一个native接口方法,此方法在C++代码中实现
public native int call();
//静态代码块,加载由C++代码生成的.dll动态链接文件(.dll相当于Java中的jar包吧...)
static{
System.loadLibrary("JNIdll");
}
}
//Test.java文件
public class Test{
public static void main(String[] args){
//创建JNI的对象call
JNI call = new JNI();
//调用call()方法;
int i = call.call();
//输出调用后的结果i
System.out.println("调用Java Native Interface,返回:"+i);
}
}
我的目录如下:
- 使用javac 编译JNI.java生成字节码文件JNI.class:
javac JNI.java
使用 javah 编译刚生成的字节码文件得到 JNI.h 文件,这里注意在使用 javah 时可能出现报错找不到类文件,试了网上的几种办法,以下方法亲测有效。
在想要存储生成的.h文件目录下 javah + -classpath + 项目包目录前 + -jni + 包名类名
比如:D:\Theta\Demo6\Test1\app\src> javah -classpath D:\Theta\Demo6\Test1\app\src\main\java -jni com.sikan.test1.JniUtils
打开 .h 文件,结构如下:
- 编写C++代码(JNIdll.cpp文件)来实现JNI.java中的call()方法(native修饰的)
//JNIdll.cpp文件
#include<stdio.h>
#include<jni.h>
#include "JNI.h"
JNIEXPORT jint JNICALL Java_JNI_call
(JNIEnv *, jobject){
//实现代码
int i = 777;
return i;
}
- 使用 vs 编译动态链接库
新建动态链接库文件,
项目的命名即为最终生成的 dll 文件的名字,这里我取名 JNIdll ,在 JNIdll.cpp 文件中写入
//JNIdll.cpp文件
#include<stdio.h>
#include<jni.h>
#include "JNI.h"
JNIEXPORT jint JNICALL Java_JNI_call
(JNIEnv *, jobject){
//实现代码
int i = 777;
return i;
}
最后尤其注意一点,先确定运行 JDK 是32位还是64位,然后选择生成配置管理器,
生成的管理器界面如下:
根据 JDK 的位数选择32位和64位,另外这里要改的地方有两处,一处在平台,另一处在活动解决方案平台。 ,另外注意要把之前生成的头文件复制到和 JNIdll.cpp 相同的目录下,还有将java目录下的include目录下的两个文件jni.h和jni_md.h(jni_md.h在include目录下的win32目录中)拷贝到vc的include目录下。最后生成动态链接库,选择生成->重新生成解决方案。
之后在项目目录下生成 JNIdll.dll 文件。
- dll 文件复制到 java library path ,具体这个路径在哪,可以通过
System.out.println(System.getProperty("java.library.path"));
获取,将 JNIdll.dll 加入到 java.library.path 即可。最后运行 test.java 返回777。
- call 函数中加入自定义类
我们想实现复杂一点的 call 函数,call 函数中有自定义类,我们首先需要定义自定义类,创建 person.h 文件,如下:
#pragma once
#ifndef PERSON_H_
#define PERSON_H_
#include <iostream>
using namespace std;
class person{//E,N,B,A,C
public:
int a;
person();
~person();
};
#endif /* PERSON_H_ */
然后创建 person.cpp 文件,如下:
#include "person.h"
#include "stdafx.h"
#include <iostream>
using namespace std;
person::person() {
a = 100000;
}
person::~person() {
}
这里注意一件事,就是所有的 cpp 文件中都要包含 stdafx.h 头文件,然后还要注意把 person.h 加入到stdafx.h 文件。如下:
// stdafx.h: 标准系统包含文件的包含文件,
// 或是经常使用但不常更改的
// 项目特定的包含文件
//
#pragma once
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容
// Windows 头文件
#include <windows.h>
#include "person.h"
// 在此处引用程序需要的其他标头
- call 函数中涉及文件读写
我们现在想在 call 文件中添加读写操作,这里注意一定要写绝对路径,要不生成的 dll 执行的时候找不到路径。
结束语
本人大三学生一枚,学识尚浅,不喜勿喷,希望今日能抛砖引玉,请各位大佬一定不吝赐教!!!