//定义
public native static String getStringFromCPP();
//调用
public static void main(String[] args) {
System.out.println(getStringFromCPP());
}
2、利用javac或者编译器直接编译,生成class文件 3、利用jdk下的javah 生成(.h)的头文件
4、将生成的头文件放置到vs新建的项目,如下; 还需要将JDK目录下的include 目录下的jni.h 和 jni_md.h文件copy到项目
因为生成的JniMain.h文件需要依赖到这两个文件,同时将JniMain.h中的 #include <jni.h>
改成 #include "jni.h"
5、创建C++或者C文件实现JniMain的方法, 这边创建JniDemo.cpp
, 引入头文件
具体代码:
#include "stdafx.h"
#include "JniMain.h"
#include <string.h>
/*
* Class: JniMain
* Method: getStringFromCPP
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_JniMain_getStringFromCPP
(JNIEnv *env, jclass jclaz) {
return env->NewStringUTF("java static method call C++ back string");
}
6、打包成dll 这边vs项目创建的是win32的项目,所以需要配置成dll 在项目右键 ->属性
由于个人的环境是64位的,所以配置管理,需要修改为x64位
生成dll
7、将dll 复制到java项目工程的根目录,并加载dll库, 运营程序
public class JniMain {
//静态方法
public native static String getStringFromCPP();
static{
System.loadLibrary("Jni");
}
public static void main(String[] args) {
System.out.println(getStringFromCPP());
}
}
结果:
java | C/C++ |
---|---|
boolean | jboolean |
byte | jbyte |
char | jchar |
short | jshort |
int | jint |
long | jlong |
float | jfloat |
double | jdouble |
java | C/C++ |
---|---|
String | jstring |
Object | jobject |
在jni调用中,返回值和参数,以及静态字段和实例字段,有对应着相应的签名,如下表格: 这些签名的时候在接下的实例讲解中会用到; 简而言之,在jni中涉及到类型的使用(包括基本类和引用类型)
方法签名例子: 方法1:
public string addTail(String tail, int index)
其对应的签名如下:
(Ljava/util/String;I)Ljava/util/String;
方法2:
public int addValue(int index, String value,int[] arr)
其对应的签名如下:
(ILjava/util/String;[I)I
public native static String getStringFromCPP();
/*
* Class: JniMain
* Method: getStringFromCPP
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_JniMain_getStringFromCPP
(JNIEnv *env, jclass jclaz) {
return env->NewStringUTF("java static method call C++ back string");
}
(JNIEnv *env, jclass jclaz)
JNIEnv
: 是jni接口调用的api指针
jclass
: 表示的就是native修饰的java静态方法所在的类public native String getStringFromCPP2();
/*
* Class: JniMain
* Method: getStringFromCPP2
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_JniMain_getStringFromCPP2
(JNIEnv *env, jobject job) {
return env->NewStringUTF("java non-static method call C++ back string ");
}
(JNIEnv *env, jobject job)
JNIEnv
: 是jni接口调用的api指针
jobject
: 表示的就是native修饰的java非静态方法所在类的对象public String key = "key";
public native void accessField(); //该native方法用于调用c++的接口访问java变量
/*
* Class: JniMain
* Method: accessField
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_JniMain_accessField
(JNIEnv *env, jobject job) {
//先获取对应的java类
jclass jclaz = env->GetObjectClass(job);
//第二个参数对应的是java的变量名,第三个是类型的签名
jfieldID fid = env->GetFieldID(jclaz, "key", "Ljava/lang/String;");
char text[100] = "Jni change string field value ";
if (fid == NULL) { // 如果字段为 NULL ,直接退出,查找失败
return;
}
// 获取字段对应的值
jstring jstr = (jstring)env->GetObjectField(job, fid);
const char * str = env->GetStringUTFChars(jstr, NULL);
strcat(text, str);
jstring key = env->NewStringUTF(text);
//修改key的值
env->SetObjectField(job, fid, key);
}
public static int count = 9;
public native void accessStaticField(); //该native方法用于调用c++的接口访问java静态变量
/*
* Class: JniMain
* Method: accessStaticField
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_JniMain_accessStaticField
(JNIEnv *env, jobject job) {
//获取类名
jclass jclaz = env->GetObjectClass(job);
//获取静态字段 第一个参数是类,第二个参数对应的是java的变量名,第三个是类型的签名
jfieldID fid = env->GetStaticFieldID(jclaz, "count", "I");
if (fid == NULL) {
return;
}
// 获取字段对应的值
jint count = env->GetStaticIntField(jclaz, fid);
count = 20;
// 修改字段的值
env->SetStaticIntField(jclaz, fid, count);
}
public class Animal {
protected String name;
public static int num = 0;
public Animal(String name) {
this.name = name;
}
//jni访问的非静态方法
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public int getNum() {
return num;
}
public static String getUID(String id) {
return "10001"+id;
}
}
//另一个类的native方法
public native void callInstanceMethod(Animal animal);
/*
* Class: JniMain
* Method: callInstanceMethod
* Signature: (LAnimal;)V
*/
JNIEXPORT void JNICALL Java_JniMain_callInstanceMethod
(JNIEnv *env, jobject instance, jobject animal) {
// 获得具体的类
jclass cls = env->GetObjectClass(animal);
// 获得具体的方法 id
jmethodID mid = env->GetMethodID(cls, "setName", "(Ljava/lang/String;)V");
if (mid == NULL) {
return;
}
//设置要穿的参数
jstring name = env->NewStringUTF("baozi");
//调用java的方法
env->CallVoidMethod(animal, mid, name);
}
public class Animal {
protected String name;
public static int num = 0;
public Animal(String name) {
this.name = name;
}
//jni访问的非静态方法
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public int getNum() {
return num;
}
//jni访问的静态方法
public static String getUID(String id) {
return "10001"+id;
}
}
//另一个类的native方法
public native String callStaticMethod(Animal animal);
/*
* Class: JniMain
* Method: callStaticMethod
* Signature: (LAnimal;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_JniMain_callStaticMethod
(JNIEnv *env, jobject instance, jobject animal) {
//获取具体的类
jclass jclz = env->FindClass("Animal");//参数也累的路径名
// 获取具体的静态方法的 id
jmethodID mid = env->GetStaticMethodID(jclz, "getUID", "(Ljava/lang/String;)Ljava/lang/String;");
if (mid == NULL) {
return env->NewStringUTF("method no found!");
}
jstring id = env->NewStringUTF("xxxxxx");
jstring result = (jstring)env->CallStaticObjectMethod(jclz, mid, id);
return result;
}
//访问构造方法
public native Date acceessConstructor();
/*
* Class: JniMain
* Method: acceessConstructor
* Signature: (LAnimal;)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_JniMain_acceessConstructor
(JNIEnv *env, jobject job) {
//通过类的路径来从JVM里面找到对应的类
jclass jclz = env->FindClass("java/util/Date");
//jmethodId 构造方法
jmethodID jmid = env->GetMethodID(jclz, "<init>","()V");
if (jmid == NULL) {
return NULL;
}
// 调用newObject 实例化Date 对象,返回值是一个jobject
jobject date_obj = env->NewObject(jclz, jmid);
// 得到对应的对象方法,前提是,我们访问了相关对象的构造函数创建了这个对象
jmethodID time_mid = env->GetMethodID(jclz, "getTime","()J");
jlong time = env->CallLongMethod(date_obj, time_mid);
printf("time: %lld \n", time);
return date_obj;
}
//整型数据在C++中进行排序
public native void giveArray(int[] inArray);
public native int[][] initInt2DArray(int size);
public native String[] initStringArray(int size);
//调用
int[] array = {3,9,2,50,6,13};
jniMain.giveArray(array);
for(int i=0; i< array.length; i++) {
System.out.println(array[i]);
}
String[] strArr = jniMain.initStringArray(5);
for (int i = 0; i < strArr.length; i++) {
System.out.println("strArr["+i+"] = "+strArr[i]);
}
int[][] intArr = jniMain.initInt2DArray(4);
for(int i =0; i < 4; i++) {
for(int j = 0; j < 3; j ++) {
System.out.println("arr["+i+"]["+j+"] = "+intArr[i][j]);
}
}
/*
* Class: JniMain
* Method: giveArray
* Signature: ([I)V
*/
JNIEXPORT void JNICALL Java_JniMain_giveArray
(JNIEnv *env, jobject job, jintArray array) {
//jintArray -> jint *
jint *elemts = env->GetIntArrayElements(array, NULL);
if (elemts == NULL)
{
return;
}
//数组长度
int len = env->GetArrayLength(array);
qsort(elemts, len, sizeof(jint), compare);
//释放可能的内存
//将JNI 修改的数据重新写回原来的内存
env->ReleaseIntArrayElements(array, elemts, JNI_COMMIT);
}
/*
* Class: JniMain
* Method: initInt2DArray
* Signature: (I)[[I
*/
JNIEXPORT jobjectArray JNICALL Java_JniMain_initStringArray
(JNIEnv *env, jobject job, jint size) {
//创建jobjectArray对象
jobjectArray result;
jclass jclz;
int i;
jclz = env->FindClass("java/lang/String");
if (jclz == NULL) {
return NULL;
}
result = env->NewObjectArray(size,jclz, job);
if (result == NULL) {
return NULL;
}
//赋值
for ( i = 0; i < size; i++)
{
char * c_str = (char *)malloc(256);
memset(c_str, 0, 256);
//将 int 转换成为 char
sprintf(c_str, "hello num: %d\n",i);
// C -> jstring
jstring str = env->NewStringUTF(c_str);
if (str == NULL) {
return NULL;
}
// 将jstring 赋值给数组
env->SetObjectArrayElement(result, i, str);
free(c_str);
c_str = NULL;
}
return result;
}
/*
* Class: JniMain
* Method: initInt2DArray
* Signature: (I)[[I
*/
JNIEXPORT jobjectArray JNICALL Java_JniMain_initInt2DArray
(JNIEnv *env, jobject job, jint size) {
//返回对象,是个二维数组
jobjectArray ret;
int i = 0;
int j = 0;
jclass intArrayClz = env->FindClass("[I");
if (intArrayClz == NULL) {
return NULL;
}
ret = env->NewObjectArray(size * 3, intArrayClz, NULL);
jint tmp[3];//固定数组
for ( i = 0; i < size; i++)
{
jintArray intArr = env->NewIntArray(3);
for ( j = 0; j < 3; j++)
{
tmp[j] = i + j;
}
env->SetIntArrayRegion(intArr, 0, 3, tmp);
//将一维数组的值复制到
env->SetObjectArrayElement(ret, i, intArr);
env->DeleteLocalRef(intArr);
}
return ret;
}
jobjectArray
表示二维数组
env->SetObjectArrayElement(ret, i, intArr);
二维数组的赋值
env->NewObjectArray(size * 3, intArrayClz, NULL);
二维数组的创建由于java的字符串编码,和C或者C++的字符串编码不一样,所以在java传中文到C/C++会出现乱码的现象
解决方法,就是在C/C++直接调用java的String来处理
//定义
public native String chineseChars2(String str);
//调用
System.out.println(jniMain.chineseChars2("宝宝22"));
/*
* Class: JniMain
* Method: chineseChars2
* Signature: (LAnimal;)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_JniMain_chineseChars2
(JNIEnv *env, jobject job, jstring in) {
char * c_str = "马蓉与宝宝";
//创建String的类
jclass jclz = env->FindClass("java/lang/String");
//获取构造函数的mid 这边使用的是java的String(byte[], string)的构造函数
jmethodID mid = env->GetMethodID(jclz, "<init>", "([BLjava/lang/String;)V");
//创建参数 jstring -> jbyteArray
jbyteArray bytes = env->NewByteArray(strlen(c_str));
// char * 赋值到byte数组中
env->SetByteArrayRegion(bytes, 0, strlen(c_str), (jbyte*)c_str);
// 设置编码
jstring charsetName = env->NewStringUTF("GB2312");
return env->NewObject(jclz, mid, bytes, charsetName);
}
public native void localRef();
/*
* Class: JniMain
* Method: localRef 局部引用
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_JniMain_localRef
(JNIEnv *env, jobject job) {
int i;
for ( i = 0; i < 5; i++)
{
jclass clz = env->FindClass("java/util/Date");
jmethodID mid = env->GetMethodID(clz, "<init>", "()V");
//创建对象
jobject date_obj = env->NewObject(clz, mid);
//使用这个引用
jmethodID time_mid = env->GetMethodID(clz, "getTime", "()J");
jlong time = env->CallLongMethod(date_obj, time_mid);
printf("local reference time: %lld \n", time);
//释放引用
env->DeleteLocalRef(clz);
env->DeleteLocalRef(date_obj);
}
}
局部引用
通过NewLocalRef和各种JNI接口创建(FindClass、NewObject、GetObjectClass和NewCharArray等)。会阻止GC回收所引用的对象,不在本地函数中跨函数使用,不能跨线前使用。函数返回后局部引用所引用的对象会被JVM自动释放,或调用DeleteLocalRef释放public native void createGlobalRef();
public native String getglobalRef();
public native void delGlobalRef();
/*
* Class: JniMain
* Method: createGlobalRef 创建全局引用
* Signature: ()V
*/
jstring global_str;
JNIEXPORT void JNICALL Java_JniMain_createGlobalRef
(JNIEnv *env, jobject job) {
jstring str = env->NewStringUTF("JNI is intersting");
global_str = (jstring)env->NewGlobalRef(str);
}
//全局引用
//跨线程,跨方法使用
// NewGlobalRef 是创建全局引用的唯一方法
/*
* Class: JniMain
* Method: getglobalRef
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_JniMain_getglobalRef
(JNIEnv *env, jobject job) {
return global_str;
}
/*
* Class: JniMain
* Method: delGlobalRef 移除全局引用
* Signature: ()V;
*/
JNIEXPORT void JNICALL Java_JniMain_delGlobalRef
(JNIEnv *env, jobject job) {
env->DeleteGlobalRef(global_str);
}
全局引用
,变量是定义在方法外,调用NewGlobalRef基于局部引用创建,会阻GC回收所引用的对象。可以跨方法、跨线程使用。JVM不会自动释放,必须调用DeleteGlobalRef手动释放env->DeleteGlobalRef(g_cls_string);public native String createWeakRef();
/*
* Class: JniMain
* Method: createWeakRef
* Signature: ()Ljava/lang/String;
*/
jstring g_weak_cls;
JNIEXPORT jstring JNICALL Java_JniMain_createWeakRef
(JNIEnv *env, jobject job) {
jclass cls_string = env->FindClass("java/lang/String");
g_weak_cls = (jstring)env->NewWeakGlobalRef(cls_string);
g_weak_cls = env->NewStringUTF("Jni weak reference");
printf("weak ref = %s \n ",g_weak_cls);
return g_weak_cls;
}
弱全局引用
:调用NewWeakGlobalRef基于局部引用或全局引用创建,不会阻止GC回收所引用的对象,可以跨方法、跨线程使用。引用不会自动释放,在JVM认为应该回收它的时候(比如内存紧张的时候)进行回收而被释放。或调用DeleteWeakGlobalRef手动释放。env->DeleteWeakGlobalRef(g_cls_string)public native void exception();
/*
* Class: JniMain
* Method: exception 异常处理
* Signature: ()V;
*/
JNIEXPORT void JNICALL Java_JniMain_exception
(JNIEnv *env, jobject job) {
jclass cls = env->GetObjectClass(job);
jfieldID fid = env->GetFieldID(cls, "key11", "Ljava/lang/String;");
//检查是否发送异常
jthrowable ex = env->ExceptionOccurred();
// 判断异常是否发送
if (ex != NULL) {
jclass newExc;
//清空JNI 产生的异常
env->ExceptionClear();
//NullPointerException
newExc = env->FindClass("java/lang/IllegalArgumentException");
if (newExc == NULL)
{
printf("exception\n");
return;
}
env->ThrowNew(newExc, "Throw exception from JNI: GetFieldID faild ");
}
}
参考:https://blog.csdn.net/qq_20404903/article/details/80662316
以上就是个人对jni的认知和总结,如有错误的地方,欢迎大家指出,该篇文件比较适合于对JNI的入门的同学 项目代码:https://github.com/jasonkevin88/JniDemo