本文共 4665 字,大约阅读时间需要 15 分钟。
新建Project 编写Hello.java类
Hello.java
package com.hqq;/** * Hello * Created by heqianqian on 2017/4/15. */public class Hello { //定义本地方法 private native void sayHello(); public static void main(String[] args) { //调用动态链接库 System.loadLibrary("Hello"); Hello hello = new Hello(); hello.sayHello(); }}
函数System.loadLibrary()是加载dll(windows)或so(Linux)库,只需名称即可,无需加入文件名后缀(.dll或.so)。
native关键字将函数sayHello()声明为本地函数,由C/C++实现。具体的实现就在hello.dll(Windows平台)或hello.so(Linux平台)中
JNI生成头文件是通过JDK中提供的javah来完成,javah在 {JDKHome}/bin目录中。用法如下:
javah -jni -classpath (搜寻类目录) -d (输出目录) (类名)
需要注意的是,使用javah来生成头文件(.h)时,-classpath指定的是编译后的java文件(.class)的目录,而不是源文件(.java)的目录,因此在使用javah指令之前,先build一下项目(或直接运行一下)。此时会生称out目录,所有编译后的文件都会存放在这个目录中。
在IDEA的控制台下输入指令
javah -classpath F:\Workspaces\JavaWorkspace\JNIDemo1\out\production\JNIDemo1 -d F:\Workspaces\JavaWorkspace\JNIDemo1\lib com.hqq.Hello
可以看到在lib文件夹下生成了com_hqq_Hello.h文件
这里注意两点
javah -jni -classpath (搜寻类目录) -d (输出目录) (类名)
生成的头文件内容如下
/* DO NOT EDIT THIS FILE - it is machine generated */#include/* Header for class com_hqq_Hello */#ifndef _Included_com_hqq_Hello#define _Included_com_hqq_Hello#ifdef __cplusplusextern "C" {#endif/* * Class: com_hqq_Hello * Method: sayHello * Signature: ()V */JNIEXPORT void JNICALL Java_com_hqq_Hello_sayHello (JNIEnv *, jobject);#ifdef __cplusplus}#endif#endif
接下来我们只需实现Java_com_huachao_java_HelloJNI_sayHello(JNIEnv *, jobject)即可。
仔细观察就会发现这个函数名称是有规律的,即
Java_ <包> _ <类名> _ <函数名>函数名> 类名> 包>
JNIEXPORT和JNICALL这两个宏定义暂时不用管。JNIEnv 和jobject后面系列文章会详细介绍,这里暂时不理会。
点击File>Settings>Tools>External Tools:
添加一个新的External Tools:
在HelloJNI.java文件中点击右键>External Tools>Generate Header File
点击生成,可以看到Terminal窗口会自动运行指令
在jni目录下新建Hello.c文件 实现Java_com_huachao_java_HelloJNI_sayHello(JNIEnv *, jobject)函数
##include#include #include"com_hqq_Hello.h"/* * Class: com_hqq_Hello * Method: sayHello * Signature: ()V */JNIEXPORT void JNICALL Java_com_hqq_Hello_sayHello(JNIEnv *env, jobject obj){ printf("hello,jni!"); printf("hello,world!"); return ;}
接下来就是使用GCC对HelloJNI.c编译,在Terminal窗口输入如下:
gcc -c jni/Hello.c
提示找不到 jni.h
F:\Workspaces\JavaWorkspace\JNIDemo1>gcc -c jni/Hello.cjni/Hello.c:1:16: fatal error: jni.h: No such file or directory #include^compilation terminated.
将JDK目录中的include目录加入
gcc -c -I"E:\Program Files\Java\jdk1.8\include" jni/Hello.c
又提示找不到 jni_md.h
F:\Workspaces\JavaWorkspace\JNIDemo1>gcc -c -I"E:\Program Files\Java\jdk1.8\include" jni/Hello.cIn file included from jni/Hello.c:1:0:E:\Program Files\Java\jdk1.8\include/jni.h:45:20: fatal error: jni_md.h: No such file or directory #include "jni_md.h"
继续将JDK目录中的include/win32加入
gcc -c -I"E:\Program Files\Java\jdk1.8\include" -I"E:\Program Files\Java\jdk1.8\include\win32" jni/Hello.c
这次运行成功 在项目根目录下生成了Hello.o
接下来是将HelloJNI.o转为HelloJNI.dll,即转为windows平台下的动态链接库
输入
gcc -Wl,--add-stdcall-alias -shared -o hello.dll Hello.o
结果项目目录中生成了hello.dll文件:
File>Settings>Tools>External Tools>+
输入内容
name:Generate DLLProgram:Parameters:-Wl,--add-stdcall-alias -I"$JDKPath$\include" -I"$JDKPath$\include\win32" -shared -o ./lib/$FileNameWithoutExtension$.dll ./jni/$FileNameWithoutExtension$.cWorking Directory:$ProjectFileDir$
这里配置将jni下.c文件生成.dll文件放在lib目录下
首先看生成dll文件前项目目录
生成之后
Exception in thread "main" java.lang.UnsatisfiedLinkError: no Hello in java.library.path at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1867) at java.lang.Runtime.loadLibrary0(Runtime.java:870) at java.lang.System.loadLibrary(System.java:1122) at com.hqq.Hello.main(Hello.java:14)
即找不到我们生成的dll文件。因为在Windows中JVM的java.library.path属性即为环境变量Path指定的目录,而我们生成的dll并未放入到Path指定的任何一个目录中,因此我们需要告诉JVM,dll文件在哪个目录中。
点击Run > Edit Configurations…,如下:
在VM options中加入java.library.path,指定dll(或so)文件所在的目录,比如本文中dll放在项目目录中的lib中,如下:
-Djava.library.path=F:\Workspaces\JavaWorkspace\JNIDemo1\lib
error: unknown type name '__int64' typedef __int64 jlong;
出现这个错误的人一般是使用Cygwin GCC的人,这是因为Cygwin GCC不认识__int64类型,找到/include/win32/jni_md.h,找到typedef __int64 jlong;并修改为:
#ifdef __GNUC__typedef long long jlong;#elsetypedef __int64 jlong;#endif
或者是编译时将__int64加入,如下:
> gcc-3 -D __int64="long long" -mno-cygwin -Wl,--add-stdcall-alias -I"\include" -I" \include\win32" -shared -o hello.dll HelloJNI.c
HelloJNI.c:1:0: sorry, unimplemented: 64-bit mode not compiled in #include
出现这个错误是因为,JDK版本是64位,而GCC编译器编译出的dll(或so)是32位,只需换个64位版本的GCC即可
参考文章: