JNI(Java Native Interface)
Java调用C/C++,C/C++调用Java的一套API
1.编写native方法
2.javah命令,生成.h头文件
3.复制.h头文件到CPP工程中
4.复制jni.h和jni_md.h文件到CPP工程中
5.实现.h头文件中声明的函数
6.生成dll文件
7.配置dll文件所在目录到环境变量
8.重启Eclipse
C的函数名称:Java_完整类名_函数名
.
.dll 共享代码
程序->打包包含 静态库 打包不会包含动态库dll
JNIEnv :代表java 运行环境,调用java中的代码
在C 中
JNIEnv 结构体指针
env C中二级指针
(*env)->NewStringUTF(env,"chegnuye");
在C++中
JNIEnv 在C++ 中就是一个结构体的别名
env C++中一级指针
env->NewStringUTF("chengyue");
在C/C++中为什么不一样?
C++ 中仍然用的C 的结构体
//JNIEnv结构体的指针别名
typedef struct JNINativeInterface_* JNIEnv;
//结构体
struct JNINativeInterface_ {
char* (NewStringUTF)(JNIEnv, char*);
};
//函数实现
char* NewStringUTF(JNIEnv* env, char* str){
//在NewStringUTF执行过程,仍然需要JNIEnv
return str;
}
void main(){
//实例化结构体
struct JNINativeInterface_ struct_env;
struct_env.NewStringUTF = NewStringUTF;
//结构体指针
JNIEnv e = &struct_env;
//结构体的二级指针
JNIEnv *env = &e;
//通过二级指针调用函数
char* str = (*env)->NewStringUTF(env,"abc");
}
java Code:
package com.test.cy;
import java.util.Date;
import java.util.Random;
import java.util.UUID;
public class JniTest {
public static void main (String args[]) {
JniTest t=new JniTest();
// String str=t.getStringFromC();//返回字符串
// System.out.print("getStringFromC"+str);
// String test=t.getString2Fromc(6);//返回字符串 带参数
// System.out.print("getString2Fromc"+test);
// System.out.print("key="+t.key+"\n");//访问 属性
// System.out.print("accrssField\n"+t.accrssField()+"\n");
// System.out.print("update key="+t.key);
// System.out.print("count="+count+"\n"); // 访问静态属性
// t.accrssStaticField();
// System.out.print("update count="+count+"\n");
// t.accessMethod(); //访问Java 方法
// t.accessStaticMethod(); //访问静态方法
Date d=t.accessConstructor(); //JNI 访问静态方法返回一个时间戳
System.out.print("d="+d);
}
public String key="vc++";
public static int count=9;
// 访问属性
public native String getStringFromC();
public native String getString2Fromc(int t);
public native String accrssField();
public native void accrssStaticField();
public native void accessMethod();
public native void accessStaticMethod();
public native Date accessConstructor();
//产生指定范围的随机数
public int genRandimInt(int max) {
System.out.print("max="+max);
return new Random().nextInt(max);
}
//产生UUID 字符串
public static String getUUID() {
return UUID.randomUUID().toString();
}
//加载动态库
static {
System.loadLibrary("Project4");
}
}
C Head:
/* DO NOT EDIT THIS FILE - it is machine generated */
include "jni.h"
/* Header for class com_test_cy_JniTest */
ifndef _Included_com_test_cy_JniTest
define _Included_com_test_cy_JniTest
ifdef __cplusplus
extern "C" {
endif
/*
- Class: com_test_cy_JniTest
- Method: getStringFromC
- Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_test_cy_JniTest_getStringFromC
(JNIEnv , jclass);
/ - Class: com_test_cy_JniTest
- Method: getString2Fromc
- Signature: ()V
*/
JNIEXPORT jstring JNICALL Java_com_test_cy_JniTest_getString2Fromc
(JNIEnv *, jobject,jint);
JNIEXPORT jstring JNICALL Java_com_test_cy_JniTest_accrssField
(JNIEnv *, jobject);
JNIEXPORT void JNICALL Java_com_test_cy_JniTest_accrssStaticField
(JNIEnv *, jobject);
JNIEXPORT void JNICALL Java_com_test_cy_JniTest_accessMethod
(JNIEnv *, jobject);
JNIEXPORT void JNICALL Java_com_test_cy_JniTest_accessStaticMethod
(JNIEnv *, jobject);
ifdef __cplusplus
}
endif
endif
C .c
define _CRT_SECURE_NO_WARNINGS
include "com_test_cy_JniTest.h"
include <Windows.h>
include <string.h>
include <stdio.h>
//函数实现
JNIEXPORT jstring JNICALL Java_com_test_cy_JniTest_getStringFromC
(JNIEnv env, jclass jcls)
{
//打开 桌面 应用
WinExec("D:\QQmusic\QQMusic.exe", 0);
//简单实现
return (env)->NewStringUTF(env, "chengyuet_wwwwTEST");
}
JNIEXPORT jstring JNICALL Java_com_test_cy_JniTest_getString2Fromc
(JNIEnv env, jobject j,jint i){
printf("\nlog---%d",i);
return (env)->NewStringUTF(env, "---" );
}
//每个native 函数 都至少有两个函数 一个是JniEnv 的指针 另一个是 jclass 或者 jobject
//当native 方法为静态方法时
//jclass 代表native 方法所属类的class 对象 (JniTest.class)
//当native 方法为非静态方法
//jobject 代表native 方法所属的对象
//Jni基本数据类型
//java基本数据类型 与 JNI基本数据类型的映射关系
//java 类型 ->jni类型 ->C 类型
//typedef unsigned char jboolean;
//typedef unsigned short jchar;
//typedef short jshort;
//typedef float jfloat;
//typedef double jdouble;
//typedef jint jsize;
//引用类型(对象)
//String jstring
//object jobject
//数组,基本数据类型的数组
//byte[] jByteArray
//对象数组
//object jobjectArray
//C/C++访问Java的成员 修改属性Key
JNIEXPORT jstring JNICALL Java_com_test_cy_JniTest_accrssField
(JNIEnv env, jobject jobject){
//得到jclass
jclass cls=(env)->GetObjectClass(env,jobject);
//得到jfieldID
jfieldID jfd = (env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");
//vc++ -> chengyue 获取这个对象的值 Get<Type>Field
jstring jstr = (env)->GetObjectField(env, jobject, jfd);
//jstring s 转成C 的字符串
//isCopy 是否复制的意思
char c_str = (env)->GetStringUTFChars(env, jstr, NULL);
//拼接成新的字符串
char text[20] = "super ";
strcat(text, c_str);
//c 字符串 转 Jstring
jstring ss = (env)->NewStringUTF(env, text);
//修改key Set<Type>Field
(env)->SetObjectField(env, jobject, jfd, ss);
return ss;
}
//访问静态属性
JNIEXPORT void JNICALL Java_com_test_cy_JniTest_accrssStaticField
(JNIEnv *env, jobject jobject){
//jclass
jclass cls = (*env)->GetObjectClass(env, jobject);
//jfield
jfieldID jfd = (*env)->GetStaticFieldID(env, cls, "count", "I");
//jInt
jint i=(*env)->GetStaticIntField(env, cls, jfd);
i++;
(*env)->SetStaticIntField(env, cls, jfd, i);
}
//访问java 方法
JNIEXPORT void JNICALL Java_com_test_cy_JniTest_accessMethod
(JNIEnv env, jobject jobject){
jclass jls=(env)->GetObjectClass(env, jobject);
jmethodID jmd = (env)->GetMethodID(env, jls, "genRandimInt", "(I)I");
//Call<Type>Method
jint i=(env)->CallIntMethod(env, jobject, jmd, 200);
printf("%ld,", i);
}
//静态方法
JNIEXPORT void JNICALL Java_com_test_cy_JniTest_accessStaticMethod
(JNIEnv env, jobject jobject){
jclass jls = (env)->GetObjectClass(env, jobject);
jmethodID jmd = (*env)->GetStaticMethodID(env, jls, "getUUID", "()Ljava/lang/String;");
//调用
jstring uuid = (*env)->CallStaticObjectMethod(env, jls, jmd);
//随机文件名称 uuid.txt
//jstring 转成 char * isCopy JNI_false Java C 操作的同一个字符串
char* uuid_name=(*env)->GetStringUTFChars(env, uuid, JNI_FALSE);
//拼接
char fileName[100];
sprintf(fileName, "F://%s.txt", uuid_name);
FILE *fp = fopen(fileName, "w");
fputs("i love code123", fp);
fclose(fp);
};
//访问构造方法
//使用java.util.date 产生一个当前的时间戳对象
JNIEXPORT jobject JNICALL Java_com_test_cy_JniTest_accessConstructor
(JNIEnv env, jobject job){
jclass cls = (env)->FindClass(env, "java/util/Date");
//jmethid 构造 方法 ()V
jmethodID constructor_mid = (env)->GetMethodID(env, cls, "<init>", "()V");
//实例化一个Date对象
jobject date_obj = (env)->NewObject(env, cls, constructor_mid);
//调用getTime方法
jmethodID mid = (env)->GetMethodID(env, cls, "getTime", "()J");
jlong time = (env)->CallLongMethod(env, date_obj, mid);
printf("\ntime:%lld\n", time);
return date_obj;
}
//访问父类的方法
JNIEXPORT void JNICALL Java_com_test_cy_JniTest_accessNonvirtualMethod
(JNIEnv env, jobject job){
//获取man 属性
jclass cls = (env)->GetObjectClass(env, job);
jfieldID jfd = (env)->GetFieldID(env, cls, "man", "Lcom/test/cy/Human;");
jobject obj = (env)->GetObjectField(env, job, jfd);
//执行sayHi 方法
jclass human_class = (env)->FindClass(env, "com/test/cy/Human");
jmethodID mid = (env)->GetMethodID(env, human_class, "sayHi", "()V");
//执行
//(env)->CallObjectMethod(env, obj, mid);
//调用父类的方法 覆盖
(env)->CallNonvirtualObjectMethod(env, obj, human_class, mid);
}
//访问父类的方法
JNIEXPORT void JNICALL Java_com_test_cy_JniTest_accessNonvirtualWomenMethod
(JNIEnv env, jobject jobjectb){
jclass jls = (env)->GetObjectClass(env, jobjectb);
jfieldID jfid = (env)->GetFieldID(env, jls, "women", "Lcom/test/cy/Human;");
jobject jobj_wo = (env)->GetObjectField(env, jobjectb, jfid);
//执行方法
jclass humon_class = (env)->FindClass(env, "com/test/cy/Human");
jmethodID mid = (env)->GetMethodID(env, humon_class, "sayHi", "()V");
//(env)->CallObjectMethod(env, jobj_wo, mid);//执行子类的方法
(env)->CallNonvirtualObjectMethod(env, jobj_wo, humon_class, mid);//执行父类的方法
}
//中文问题
JNIEXPORT jstring JNICALL Java_com_test_cy_JniTest_chineseChars
(JNIEnv *env, jobject jo, jstring js){
//输出
char jsd = (env)->GetStringUTFChars(env, js, JNI_FALSE);
printf("%s\n", jsd);
//返回中文異常
char str = "马伊意义嘛嘛嘛";
//return (env)->NewStringUTF(env,str);
//執行 String 需要的条件
//1.jmethodID
//2. byte 数组 字符数组 编码
jclass jls=(*env)->FindClass(env, "java/lang/String");
jmethodID jmd=(*env)->GetMethodID(env, jls,"<init>", "([BLjava/lang/String;)V");
//jbyte ->char 相对应
jbyteArray jba = (*env)->NewByteArray(env, strlen(str));
//byte 数组赋值
(*env)->SetByteArrayRegion(env, jba, 0, strlen(str), str);
//字符编码jstring
jstring charsetName = (*env)->NewStringUTF(env,"GB2312");
return (*env)->NewObject(env, jls, jmd, jba, charsetName);
}
int compare(int a, int b){
return (b) - (a);
}
//数组
JNIEXPORT void JNICALL Java_com_test_cy_JniTest_giveArray
(JNIEnv env, jobject jo, jintArray jia){
// jintArray ->jint 指针 -》 c int 数组
jint elems=(env)->GetIntArrayElements(env, jia, NULL);
//数组长度
int len = (env)->GetArrayLength(env, jia);
//函数指针
//数组排序
qsort(elems, len, sizeof(jint), compare);
//同步 mode 0: java 数组进行更新,并且释放从C/C++数组
// model 1: JNI_ABORT : java 数组不更新。释放从C/ C++数组
// model 2: JNI_COMMENT: java 数组进行更新。不释放从C/ C++数组
(*env)->ReleaseIntArrayElements(env, jia, elems,0);
}
//返回数组
JNIEXPORT jintArray JNICALL Java_com_test_cy_JniTest_getArray
(JNIEnv env, jobject jo, jint ji){
jintArray jia = (env)->NewIntArray(env,ji);
jint element = (env)->GetIntArrayElements(env, jia, NULL);
int i = 0;
for (; i < ji; i++){
element[i] = i;
}
(*env)->ReleaseIntArrayElements(env, jia, element, 0);
return jia;
}
//JNI引用变量
//引用类型:局部引用和全局引用
//作用:在JNI中告诉虚拟机合适回收一个JNI变量
//局部引用 通过deleteLocalRef()手动释放对象
// 1.访问一个很大的java对象,使用完后还要进行复杂的操作。
// 2.创建了太多的局部引用,并且和后边代码没有关联性
//模拟:循环创建数组
JNIEXPORT void JNICALL Java_com_test_cy_JniTest_localReference
(JNIEnv env, jobject jo){
int i=0;
for (; i < 5; i++){
jclass cla = (env)->FindClass(env, "java/util/Date");
jmethodID mid = (env)->GetMethodID(env, cla, "<init>", "()V");
jobject obj = (env)->NewObject(env,cla,mid);
printf("%x#\n", &obj);
//不在使用这些对象
//释放内存 通知垃圾回收器 回收对象
(*env)->DeleteLocalRef(env, obj);
}
}
//全局引用
//多个方法里边可以去使用
//共享 跨多个线程 手动控制内存的使用 可以手动释放
jstring globalStr;
//创建
JNIEXPORT void JNICALL Java_com_test_cy_JniTest_createglobalReference
(JNIEnv env, jobject jo){
jstring str = (env)->NewStringUTF(env, "chengyue123");
globalStr = (*env)->NewGlobalRef(env, str);
}
//获取
JNIEXPORT jstring JNICALL Java_com_test_cy_JniTest_getglobalReference
(JNIEnv *env, jobject jo){
return globalStr;
}
//释放
JNIEXPORT void JNICALL Java_com_test_cy_JniTest_deleteglobalReference
(JNIEnv env, jobject jo){
(env)->DeleteGlobalRef(env, globalStr);
}
//弱全局引用
//在内存不足时释放所引用的对象,引用一个不常用的对象,如果为NULL 则重新创建弱引用。
jstring globalwearRef;
//创建
JNIEXPORT void JNICALL Java_com_test_cy_JniTest_createglobalWeakReference
(JNIEnv env, jobject jo){
jstring str = (env)->NewStringUTF(env, "chengyue123");
globalwearRef = (*env)->NewWeakGlobalRef(env, str);
}
//获取
JNIEXPORT jstring JNICALL Java_com_test_cy_JniTest_getglobalWeakReference
(JNIEnv *env, jobject jo){
return globalwearRef;
}
//释放
JNIEXPORT void JNICALL Java_com_test_cy_JniTest_deleteglobalWeakReference
(JNIEnv env, jobject jo){
(env)->DeleteWeakGlobalRef(env, globalwearRef);
}
//异常处理
//1. 保证Java代码可以继续执行
//2. 补救措施 保证后边 C /C++的代码可以继续执行
//JNI 自己抛出的异常在Java 层无法被捕捉到,只能在C 层清空
//用户通过ThrowNewke可以在java层显示
JNIEXPORT void JNICALL Java_com_test_cy_JniTest_exception
(JNIEnv env, jobject jo){
jclass jla = (env)->GetObjectClass(env, jo);
jfieldID jfid = (env)->GetFieldID(env, jla, "key2", "Ljava/lang/String;");
//检测是否发生异常 后边java 代码继续执行
jthrowable exception = (env)->ExceptionOccurred(env);
if (exception!=NULL){
//让java 代码继续执行
(env)->ExceptionClear(env);
//补救措施
jfid = (env)->GetFieldID(env, jla, "key", "Ljava/lang/String;");
}
//获取属性的值
jstring jstr = (env)->GetObjectField(env, jo, jfid);
char str = (env)->GetStringUTFChars(env, jstr, NULL);
//对比属性值是否合法
if (_stricmp(str, "vc++") != 0){
//抛出异常 给Java 处理
jclass jlsa = (env)->FindClass(env, "java/lang/IllegalArgumentException");
(*env)->ThrowNew(env, jlsa, "kay is invalid");
}
}
网友评论