1.编写JAVA代码
新建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平台)中
2.生成JNI头文件
1 ) 手动输入javah指令
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 (输出目录) (类名)
- 搜索类目录不要进入包目录 否则会提示找不到类
- 类名要写全且不用加.java后缀
生成的头文件内容如下
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_hqq_Hello */
#ifndef _Included_com_hqq_Hello
#define _Included_com_hqq_Hello
#ifdef __cplusplus
extern "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后面系列文章会详细介绍,这里暂时不理会。
2 ) 一键生成头文件
点击File>Settings>Tools>External Tools:
添加一个新的External Tools:
在HelloJNI.java文件中点击右键>External Tools>Generate Header File
点击生成,可以看到Terminal窗口会自动运行指令
3. 编写C文件并编译成dll(或so)文件
命令行生成dll
在jni目录下新建Hello.c文件 实现Java_com_huachao_java_HelloJNI_sayHello(JNIEnv *, jobject)函数
##include<jni.h>
#include<stdio.h>
#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.c
jni/Hello.c:1:16: fatal error: jni.h: No such file or directory
#include<jni.h>
^
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.c
In 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文件:
一键生成dll
File>Settings>Tools>External Tools>+
输入内容
name:Generate DLL
Program:<GCC路径>
Parameters:-Wl,--add-stdcall-alias -I"$JDKPath$\include" -I"$JDKPath$\include\win32" -shared -o ./lib/$FileNameWithoutExtension$.dll ./jni/$FileNameWithoutExtension$.c
Working Directory:$ProjectFileDir$
这里配置将jni下.c文件生成.dll文件放在lib目录下
首先看生成dll文件前项目目录
生成之后
4. 运行
5.可能出现的错误
java.library.path找不到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
无法识别__int64类型错误
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;
#else
typedef __int64 jlong;
#endif
或者是编译时将__int64加入,如下:
> gcc-3 -D __int64="long long" -mno-cygwin -Wl,--add-stdcall-alias
-I"<JAVA_HOME>\include" -I"<JAVA_HOME>\include\win32" -shared -o hello.dll HelloJNI.c
64-bit mode not compiled
HelloJNI.c:1:0: sorry, unimplemented: 64-bit mode not compiled in
#include <jni.h>
出现这个错误是因为,JDK版本是64位,而GCC编译器编译出的dll(或so)是32位,只需换个64位版本的GCC即可
参考文章: