提示:文章
文章目录
前言
前期疑问:
本文目标:
一 背景
之前搞过jni,之前是强哥指导搞的,现在感觉又忘了。
今天照着帖子再搞一次。参考帖子:https://blog.csdn.net/youmingyu/article/details/53447118
二 jni实际使用
2.1 查看javac是否安装
使用java -version查看版本
C:\Users>javac -version
javac 1.8.0_262
我认为只要是装了jdk应该就有了javac。
## 2.2 windows下JAVA调用C/C++库
2.2.1 编写java代码
class HelloJNI
{
//注意displayHelloJNI()方法的声明,它有一个关键字native,表明这个方法使用java以外的语言实现。这个方法不包括实现,因为我们要用c/c++语言实现它。
public native int displayHelloJNI(int a,int b); //声明外部实现函数
//注意System.loadLibrary("helloJNILib")这句代码,它是在静态初始化块中定义的,用来装载helloJNILib共享库,这就是我们在后面生成的helloJNILib.dll(假如在其他的操作系统可能是其他的形式,比如Linux下为helloJNILib.so)
static {
System.loadLibrary("win64/helloJNILib"); //导入本地库
}
public static void main(String[] args) {
System.out.println(new HelloJNI().displayHelloJNI(1,2));
}
}
2.2.2 javac生成.class文件
执行javac HelloJNI.java
代码中的注释一部分是我从网页中直接拷贝的,在执行javac HelloJNI.java指令时,会报错如下
C:\Users\\Desktop\HelloJNIPro>javac HelloJNI.java
HelloJNI.java:3: 错误: 编码GBK的不可映射字符
//娉ㄦ剰displayHelloJNI()鏂规硶鐨勫0鏄庯紝瀹冩湁涓?涓叧閿瓧native锛岃〃鏄庤繖涓柟娉曚娇鐢╦ava浠ュ鐨勮瑷?瀹炵幇銆傝繖涓柟娉曚笉鍖呮嫭瀹炵幇锛屽洜涓烘垜浠鐢╟/c++璇█瀹炵幇瀹冦??
解决办法:在notepad中——编码——转为ANSI编码——保存
再次执行javac HelloJNI.java可以生成HelloJNI.class文件
2.2.3 javah生成.h文件
执行javah HelloJNI
2.2.4 编写C/C++本地实现代码
#include <jni.h> //导入jni头文件
#include "HelloJNI.h" //导入jni头文件
#include <stdio.h>
JNIEXPORT jint JNICALL Java_HelloJNI_displayHelloJNI(JNIEnv *env, jobject obj,jint a,jint b){
printf("Hello JNI!\n");
int c=a+b;
return c;
}
2.2.4 创建vs项目
创建vs项目helloJNILib,新建项目——windows桌面——控制台应用
将上述helloJNI.cpp代码拷贝到工程中,头文件中添加helloJNI.h文件
设置引用文件路径,jni.h在JAVA_HOME/include里面
VS 添加include头文件
👉添加工程的头文件目录:
- 当前工程 -> 右键“属性" -> “配置属性" -> “C/C++” -> “常规" -> “附加包含目录" ==> 添加上该工程的头文件存放目录即可。
- 修好后改好之后,可能还会出现找不到头文件的提示。将平台改成电脑对应的x64
修改配置类型为动态库(.dll)
生成dll动态库文件
将dll文件拷贝到win64文件夹中,执行java HelloJNI指令可以成功执行
执行java helloJNI
运行结果
2.2.5 IDEA中使用dll
比想象中的简单,直接IDEA选中HelloJNI文件夹,直接运行
至于为什么dll文件需要放在win64文件夹下,是因为这条语句
System.loadLibrary("win64/helloJNILib"); //导入本地库
2.3 linux下JAVA调用C/C++库
windows环境是编译成.dll文件
linux环境下是编译成.so文件
将vs工程下的文件拷贝到linux服务器中,其中增加include文件夹,文件夹中增加jni.h文件和jni_md.h文件。
文件情况如图
编写makefile文件
CXXFLAGS = -Werror=return-type -std=c++14 -Wl,-z,relro,-z,now,-z,noexecstack -D_FORTIFY_SOURCE=2 -O2 -s -ftrapv -fPIC -fstack-protector-all -w -c -shared -o -D__STDC_LIMIT_MACROS -Dlinux -I. \
CXX = g++
C = gcc
SRC_DIR = ./
TARGET_DIR = ../build/bin/linux
TARGET_FILE = helloJNI.so
SRC_CXX=${wildcard $(SRC_DIR)/*.cpp}
SRC_CC=${wildcard $(SRC_DIR)/*.cc}
OBJ=$(SRC_CXX:.cpp=.o)
OBJ_CC=$(SRC_CC:.cc=.o)
CXXFLAGS_ALL = -Wl,-z,relro,-z,now,-z,noexecstack -D_FORTIFY_SOURCE=2 -O2 -s -ftrapv -fPIC -fstack-protector-all -static-libstdc++
all: $(OBJ) $(OBJ_CC)
$(CXX) $(CXXFLAGS_ALL) -shared -o $(TARGET_FILE) $(OBJ) $(OBJ_CC) $(LIBS)
mkdir -p $(TARGET_DIR)
cp $(TARGET_FILE) $(TARGET_DIR)/$(TARGET_FILE)
clean:
rm -f $(SRC_DIR)/*.o
rm -f $(TARGET_FILE)
rm -f $(TARGET_DIR)/$(TARGET_FILE)
%.o: %.cpp
$(CXX) $(CXXFLAGS) $*.cpp -o $@
执行make,编译成功
root@0002:~/gdal_0407/helloJNI# make
g++ -Werror=return-type -std=c++14 -Wl,-z,relro,-z,now,-z,noexecstack -D_FORTIFY_SOURCE=2 -O2 -s -ftrapv -fPIC -fstack-protector-all -w -c -shared -o -D__STDC_LIMIT_MACROS -Dlinux -I. -I./include helloJNILib.cpp -o helloJNILib.o
g++ -Wl,-z,relro,-z,now,-z,noexecstack -D_FORTIFY_SOURCE=2 -O2 -s -ftrapv -fPIC -fstack-protector-all -static-libstdc++ -shared -o helloJNI.so .//helloJNILib.o
mkdir -p ../../../build/bin/linux
cp helloJNI.so ../../../build/bin/linux/helloJNI.so
make clean可以清除编译文件。
以上在linux环境下编译so文件成功
三 针对有包的java文件生成jni接口
3.1 第一点
生成jni接口的java文件可以没有main函数,比如像下面的java代码、
//testJni.java
package com.google.test.jni;
class TestJni
{
public native boolean getCalculateData(String path);
}
如果没有package的包引入,可以直接使用javac和javah生成jni接口
这边描述的是针对引入包的情况,引入包之后使用javac是正常使用的。但是使用javah会报错:
需要将编译出的.class文件放在包对应的文件夹下,com.huawei.map.trafficlight.jni,然后再使用javah指令编译出头文件。
具体如下:
创建testJni.java文件,打开cmd,进入testJni的路径,执行
javac testJni.java
因为程序引入包package,创建多级文件夹com.google.test.jni,将testJni.java文件拷贝到com.google.test.jni文件夹下
执行
javah com.google.test.jni.TestJni
编译出的头文件在test_jni路径中
总结
未完待续