1 简介
网上有很多教程,但是很多都没有一个完整的实例,我在学习的时候,踩了不少坑。 我们使用JNI的目的,无非就是想要用调用C++的接口,并且最好JAVA也能为C++提供一个接口(用于回调),这两个目的都很重要。尤其后面那个目的,如果要实现异步操作,那么java必须能给C++提供接口
2 类型的映射关系
JNI 它通过类型映射,把JAVA中的基本数据类型,映射到了C++中,这个映射关系记录在了jni.h文件里(该文件在JDK根路径/include下) 所以,在C++中,为了兼容JNI,你要尽量使用jni.h中定义的数据类型。 以下是映射关系(左侧为Java,右侧为jni.h中定义的)
boolean(布尔型)-》jboolean,无符号8个比特 byte(字节型) -》jbyte,有符号8个比特 char(字符型)-》char,无符号16个比特 short(短整型)-》jshort,有符号16个比特 int(整型)-》jint,有符号32个比特 long(长整型)-》jlong,有符号64个比特 float(浮点型),jfloat,32个比特 double(双精度浮点型)-》jdouble,64个比特 void(空型)-》void,N/A 注意
在JAVA中,很多方法的形参是包装类型。在C++中,使用包装类型不与基本数据类型兼容!!!如果java方法中的形参是Integer,在C++中你不能使用 jint 来调用!你必须在C++中手动创建对象(jobect类型)!!创建对象挺麻烦的。。
3 一个实例
3.1 写JAVA的接口
在JAVA中新建一个工程,写一个Demo类。 注意包名和类名,在C++中写的方法会通过名称映射到指定的native方法上,比如C++ 中的Java_com_wu_jni_Demo_callback 方法指的是com.wu.jni.Demo 类中的callback方法。
package com. wu. jni;
public class Demo {
public native void sayHello ( ) ;
}
我们需要把该Demo.java文件手动编译一下。 打开文件的路径,打开命令提示行(CMD),然后输入
javac Demo.java cd 到src/main/java目录下 javap -p -s com.wu.jni.Demo 这里会得到com_wu_jni_Demo.h这个文件,这个文件后面我们要用到
3.2 写 C++ 接口
打开VS2019,新建一个DLL项目
新建好项目后,不要动自动创建的文件。 下面是关键了
将上面得到的com_wu_jni_Demo.h文件放入项目目录下 新建一个.cpp文件,名称随便起,需要调用#include "com_wu_jni_Demo.h"这个文件 还需要将【jdk根路径/include/jni.h】和【jdk根路径/include/win32/jni_md.h】这两个文件复制到项目中 将com_wu_jni_Demo.h 文件中的 #include <jni.h> 改为#include “jni.h” 编写刚才创建的.cpp文件
#include "pch.h"
#include <utility>
#include <limits.h>
#include <iostream>
#include "com_wu_jni_Demo.h"
using namespace std;
JNIEXPORT void JNICALL Java_com_wu_jni_Demo_sayHello ( JNIEnv* env, jobject obj) {
cout << "Hello World" << endl;
}
注意输出的模式,右键项目 - 生成 在解决方案/x64/Release下找到生成的.dll文件
3.3 编写java的入口
将上面生成的.dll文件放到我们的java项目的rescources文件夹下 接下来我们就可以在java中调用了
public static void main ( String[ ] args) throws UnsupportedEncodingException {
URL url = Demo. class . getResource ( "/JniDll.dll" ) ;
String filePath = URLDecoder. decode ( url. getPath ( ) , "utf8" ) ;
System. load ( filePath) ;
Demo demo = new Demo ( ) ;
demo. sayHello ( ) ;
}
4 C++中使用jobject对象
jobject 对应 java 中的对象。在native方法中,只要不是基本数据类型的形参或返回值,都会被变成jobject对象,以便在C++中使用。 但是使用它比较繁琐。。。。 让我们在java中新创建两个native方法
package com. wu. jni;
import java. util. function. Function;
public class Demo {
public native void sayHello ( ) ;
public native void add ( int a, double b) ;
public native void callback ( Function< Integer, Integer> fun) ;
}
还是按照刚才的套路,通过 javac Demo.java 获得.class文件 在通过src\main\java路径下,通过 javap -s -p com.wu.jni.Demo 生成com_wu_jni_Demo.h文件 将文件中的内容复制,粘贴到刚才VS2019DLL项目中的com_wu_jni_Demo.h文件里 在DLL项目的.cpp文件中,我们新增两个方法
#include "pch.h"
#include <utility>
#include <limits.h>
#include <iostream>
#include "com_wu_jni_Demo.h"
using namespace std;
JNIEXPORT void JNICALL Java_com_wu_jni_Demo_sayHello ( JNIEnv* , jobject) {
cout << "Hello World" << endl;
}
JNIEXPORT void JNICALL Java_com_wu_jni_Demo_add ( JNIEnv* , jobject, jint a, jdouble b) {
cout << a << endl;
cout << b << endl;
}
JNIEXPORT void JNICALL Java_com_wu_jni_Demo_callback ( JNIEnv* env, jobject, jobject call) {
jclass cls = env- > FindClass ( "java/util/function/Function" ) ;
jclass cls2 = env- > FindClass ( "java/lang/Integer" ) ;
jmethodID param1_mid = env- > GetMethodID ( cls2, "<init>" , "(I)V" ) ;
jint param1Temp = 10 ;
jobject param1 = env- > NewObject ( cls2, param1_mid, param1Temp) ;
jmethodID mid = env- > GetMethodID ( cls, "apply" , "(Ljava/lang/Object;)Ljava/lang/Object;" ) ;
env- > CallObjectMethod ( call, mid, param1) ;
}
然后生成解决方案 将生成的DLL文件放到JAVA项目的rescourses目录下,加载它,调用对应的类方法即可
public static void main ( String[ ] args) throws UnsupportedEncodingException {
URL url = Demo. class . getResource ( "/JniDll.dll" ) ;
String filePath = URLDecoder. decode ( url. getPath ( ) , "utf8" ) ;
System. load ( filePath) ;
Demo demo = new Demo ( ) ;
demo. sayHello ( ) ;
demo. add ( 10 , 15 ) ;
demo. callback ( param1- > {
System. out. println ( "猜猜我是几?" + param1) ;
System. out. println ( "啊哈哈哈哈" ) ;
return null;
} ) ;
System. out. println ( "哈哈" ) ;
}
5 后记
第4节中体现了jobject的创建过程,以及调用它对应方法的过程。 关于方法签名 让我们来看看Function的方法签名
javap -s -p java.util.function.Function
Compiled from "Function.java"
public interface java. util. function. Function< T, R> {
public abstract R apply ( T) ;
descriptor: ( Ljava/ lang/ Object; ) Ljava/ lang/ Object;
public < V> java. util. function. Function< V, R> compose ( java. util. function. Function< ? super V, ? extends T> ) ;
descriptor: ( Ljava/ util/ function/ Function; ) Ljava/ util/ function/ Function;
public < V> java. util. function. Function< T, V> andThen ( java. util. function. Function< ? super R, ? extends V> ) ;
descriptor: ( Ljava/ util/ function/ Function; ) Ljava/ util/ function/ Function;
public static < T> java. util. function. Function< T, T> identity ( ) ;
descriptor: ( ) Ljava/ util/ function/ Function;
private static java. lang. Object lambda$identity$2 ( java. lang. Object) ;
descriptor: ( Ljava/ lang/ Object; ) Ljava/ lang/ Object;
private java. lang. Object lambda$andThen$1 ( java. util. function. Function, java. lang. Object) ;
descriptor: ( Ljava/ util/ function/ Function; Ljava/ lang/ Object; ) Ljava/ lang/ Object;
private java. lang. Object lambda$compose$0 ( java. util. function. Function, java. lang. Object) ;
descriptor: ( Ljava/ util/ function/ Function; Ljava/ lang/ Object; ) Ljava/ lang/ Object;
}
让我们来看看 Integer 的方法签名
javap -s -p java.lang.Integer
Compiled from "Integer.java"
public final class java . lang. Integer extends java. lang. Number implements java. lang. Comparable< java. lang. Integer> {
public static final int MIN_VALUE;
descriptor: I
public static final int MAX_VALUE;
descriptor: I
public static final java. lang. Class< java. lang. Integer> TYPE;
descriptor: Ljava/ lang/ Class;
static final char [ ] digits;
descriptor: [ C
static final char [ ] DigitTens;
descriptor: [ C
static final char [ ] DigitOnes;
descriptor: [ C
static final int [ ] sizeTable;
descriptor: [ I
private final int value;
descriptor: I
public static final int SIZE;
descriptor: I
public static final int BYTES;
descriptor: I
private static final long serialVersionUID;
descriptor: J
public static java. lang. String toString ( int , int ) ;
descriptor: ( II) Ljava/ lang/ String;
public static java. lang. String toUnsignedString ( int , int ) ;
descriptor: ( II) Ljava/ lang/ String;
public static java. lang. String toHexString ( int ) ;
descriptor: ( I) Ljava/ lang/ String;
public static java. lang. String toOctalString ( int ) ;
descriptor: ( I) Ljava/ lang/ String;
public static java. lang. String toBinaryString ( int ) ;
descriptor: ( I) Ljava/ lang/ String;
private static java. lang. String toUnsignedString0 ( int , int ) ;
descriptor: ( II) Ljava/ lang/ String;
static int formatUnsignedInt ( int , int , char [ ] , int , int ) ;
descriptor: ( II[ CII) I
public static java. lang. String toString ( int ) ;
descriptor: ( I) Ljava/ lang/ String;
public static java. lang. String toUnsignedString ( int ) ;
descriptor: ( I) Ljava/ lang/ String;
static void getChars ( int , int , char [ ] ) ;
descriptor: ( II[ C) V
static int stringSize ( int ) ;
descriptor: ( I) I
public static int parseInt ( java. lang. String, int ) throws java. lang. NumberFormatException;
descriptor: ( Ljava/ lang/ String; I) I
public static int parseInt ( java. lang. String) throws java. lang. NumberFormatException;
descriptor: ( Ljava/ lang/ String; ) I
public static int parseUnsignedInt ( java. lang. String, int ) throws java. lang. NumberFormatException;
descriptor: ( Ljava/ lang/ String; I) I
public static int parseUnsignedInt ( java. lang. String) throws java. lang. NumberFormatException;
descriptor: ( Ljava/ lang/ String; ) I
public static java. lang. Integer valueOf ( java. lang. String, int ) throws java. lang. NumberFormatException;
descriptor: ( Ljava/ lang/ String; I) Ljava/ lang/ Integer;
public static java. lang. Integer valueOf ( java. lang. String) throws java. lang. NumberFormatException;
descriptor: ( Ljava/ lang/ String; ) Ljava/ lang/ Integer;
public static java. lang. Integer valueOf ( int ) ;
descriptor: ( I) Ljava/ lang/ Integer;
public java. lang. Integer ( int ) ;
descriptor: ( I) V
public java. lang. Integer ( java. lang. String) throws java. lang. NumberFormatException;
descriptor: ( Ljava/ lang/ String; ) V
public byte byteValue ( ) ;
descriptor: ( ) B
public short shortValue ( ) ;
descriptor: ( ) S
public int intValue ( ) ;
descriptor: ( ) I
public long longValue ( ) ;
descriptor: ( ) J
public float floatValue ( ) ;
descriptor: ( ) F
public double doubleValue ( ) ;
descriptor: ( ) D
public java. lang. String toString ( ) ;
descriptor: ( ) Ljava/ lang/ String;
public int hashCode ( ) ;
descriptor: ( ) I
public static int hashCode ( int ) ;
descriptor: ( I) I
public boolean equals ( java. lang. Object) ;
descriptor: ( Ljava/ lang/ Object; ) Z
public static java. lang. Integer getInteger ( java. lang. String) ;
descriptor: ( Ljava/ lang/ String; ) Ljava/ lang/ Integer;
public static java. lang. Integer getInteger ( java. lang. String, int ) ;
descriptor: ( Ljava/ lang/ String; I) Ljava/ lang/ Integer;
public static java. lang. Integer getInteger ( java. lang. String, java. lang. Integer) ;
descriptor: ( Ljava/ lang/ String; Ljava/ lang/ Integer; ) Ljava/ lang/ Integer;
public static java. lang. Integer decode ( java. lang. String) throws java. lang. NumberFormatException;
descriptor: ( Ljava/ lang/ String; ) Ljava/ lang/ Integer;
public int compareTo ( java. lang. Integer) ;
descriptor: ( Ljava/ lang/ Integer; ) I
public static int compare ( int , int ) ;
descriptor: ( II) I
public static int compareUnsigned ( int , int ) ;
descriptor: ( II) I
public static long toUnsignedLong ( int ) ;
descriptor: ( I) J
public static int divideUnsigned ( int , int ) ;
descriptor: ( II) I
public static int remainderUnsigned ( int , int ) ;
descriptor: ( II) I
public static int highestOneBit ( int ) ;
descriptor: ( I) I
public static int lowestOneBit ( int ) ;
descriptor: ( I) I
public static int numberOfLeadingZeros ( int ) ;
descriptor: ( I) I
public static int numberOfTrailingZeros ( int ) ;
descriptor: ( I) I
public static int bitCount ( int ) ;
descriptor: ( I) I
public static int rotateLeft ( int , int ) ;
descriptor: ( II) I
public static int rotateRight ( int , int ) ;
descriptor: ( II) I
public static int reverse ( int ) ;
descriptor: ( I) I
public static int signum ( int ) ;
descriptor: ( I) I
public static int reverseBytes ( int ) ;
descriptor: ( I) I
public static int sum ( int , int ) ;
descriptor: ( II) I
public static int max ( int , int ) ;
descriptor: ( II) I
public static int min ( int , int ) ;
descriptor: ( II) I
public int compareTo ( java. lang. Object) ;
descriptor: ( Ljava/ lang/ Object; ) I
static { } ;
descriptor: ( ) V
}