实际中,java语言有时还是会需要编写或使用其他语言的代码,这种代码通常成为本地代码
用java调用C/C++函数
通过Java调用C++代码编写的 *.dll 类库中封装的方法,本 Demo 中包含两个方法,一个是输出文本信息,无返回值;一个计算并返回两个整数之和。
1. 编写Java接口
package package test.test_1;
public class test_1{
public native void sendMess(String mess);
public native int plusNum(int a,int b);
}
编写完成后,生成 .class 文件。
2. 生成 *.h 头文件
进入 cmd 命令行,使用上一步生成的 .class 文件,利用 jdk 的 javah 命令生成 *.h 头文件
# 我的包名: package test.test_1
# javah
# -classpath :为.class 文件所在的根路径: E:\java\eclipse_wokespace\test_1\bin\
# .class 文件的完整路径为E:\java\eclipse_wokespace\test_1\bin\test\test_1\test_1.class
# -d : 输出 *.h 头文件的路径: E:\java\eclipse_wokespace\
# -jni : 生成JNI样式的包头文件,可以理解成 *.class 文件的 包路径+类名
# -jni 必须是*.class 文件的 包路径+类名,否则会报错。
进入文件夹:
E:\java\eclipse_wokespace\test_1\bin\test\test_1>
输入cmd命令:
javah -classpath E:\java\eclipse_wokespace\test_1\bin -d E:\java\eclipse_wokespace\ -jni test.test_1.test_1
生成的test.test_1.test_1.h文件如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class test_test_1_test_1 */
#ifndef _Included_test_test_1_test_1
#define _Included_test_test_1_test_1
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: test_test_1_test_1
* Method: sendMess
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_test_test_11_test_11_sendMess
(JNIEnv *, jobject, jstring);
/*
* Class: test_test_1_test_1
* Method: plusNum
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_test_test_11_test_11_plusNum
(JNIEnv *, jobject, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
3. 编写C++代码
1) 创建一个C++项目 ,名字:jni_1 ,设置应用类型为 dll
2) 将 jdk 安装目录下的 jni.h 、 jni_md.h 以及之前生成的 com_daniel_JNI_Test.h 放入项目根路径下的 include 文件夹 (需自己创建文件夹)
3) 添加项目VC++目录的包含目录,将上一步创建的 include 文件夹包含进项目。
4) 创建 test_jni.cpp 文件,并编写 c++ 代码实现,其中的方法头,可以从 生成的test.test_1.test_1.h文件中拷贝。 通过java来生成头文件从而获取描述符
# include<iostream>
# include<test_test_1_test_1.h>
using namespace std;
JNIEXPORT void JNICALL Java_test_test_11_test_11_sendMess
(JNIEnv *env, jobject obj, jstring str){
char* t;
t = (char*)env->GetStringUTFChars(str, 0);
cout << t << endl;
}
JNIEXPORT jint JNICALL Java_test_test_11_test_11_plusNum
(JNIEnv *env, jobject obj, jint ia, jint ib){
return ia + ib;
}
运行C++项目的生成按钮,并在生成路径下找到生成的 dll 文件(本文为 jni_1.dll)。生成的一定要是64位的dll。
4. Java调用C++ dll
将生成的 *.dll 添加到Java项目的 Native Library 中,修改最初的 JNI_Test.java 的代码,调用 dll 中的方法。
package test.test_1;
public class test_1{
public native void sendMess(String mess);
public native int plusNum(int a,int b);
static {
System.loadLibrary("jni_1");
}
public static void main(String[] args) {
test_1 tt= new test_1();
tt.sendMess("Hi, FZX");
System.out.println(tt.plusNum(5, 6));
}
}
5. 添加知识点
jni中字段描述符:
在C与java编程语言之间传递参数时,由于这两种语言对数字类型的实现不同,所以需要指定他们彼此之间的对应类型,数据类型之间的对应关系如下 :
int->jint,long->jlong,byte->jbyte,char->jchar,short->jshort,
float->jfloat,double->jdouble,boolean->jboolean
对于字符串参数,需要考虑编程语言之间的编码方式,java中的字符串是UTF-16编码点的序列,而C的字符串则是以null结尾的字节序列。java本地接口有两组操作字符串的函数,一组是把java字符串转换成改良的UTF-8字节序列,另一组将它们转换成UTF-16数值的数组,也就是转换成jchar数组。
jstring NewStringUTF(JNIEnv* env,const char bytes[])
//根据改良的UTF-8字节序列,返回一个新的java字符串,当字符串无法构造时,返回null
jsize getStringUTFLength(JNIEnv* env,jstring string)
//返回进行UTF-8编码所需的字节数
const ibyte* GetStringUTFChars(JNIEnv* env,jstring string,jboolean* isCopy)
//返回改良UTF-8编码的字符串的指针,直到ReleaseStringUTFChars函数调用前该指针一直有效的。
void ReleaseStringUTFChars(JNIEnv* env,jstring string,const ibyte bytes[])
//通知虚拟机本地代码不再需要通过bytes访问Java字符串
void GetStringRegion(JNIEnv * env,jstring string,jsize start,jsize length,jchar *buffer)
//将一个Unicode字符序列从字符串复制到用户提供的缓存中。
void GetStringUTFREdion(JNIEnv * env,jstring string,jsize start,jsize length,jchar *buffer)
//将一个改良的UTF-8字符序列从字符串复制到用户提供的缓存中。
参考博客:
https://blog.youkuaiyun.com/danielpei1222/article/details/62462497
https://blog.youkuaiyun.com/baidu_37464759/article/details/54915476