JNI(Java Native Interface)
Java调用C/C++,C/C++调用Java的一套API
windows下步骤
1.编写native方法
2.javah命令,生成.h头文件
3.复制.h头文件到CPP工程中
4.复制jni.h和jni_md.h文件到CPP工程中
5.实现.h头文件中声明的函数
6.生成dll文件
7.配置dll文件所在目录到环境变量
8.重启Eclipse
基本数据:Java基本数据类型与JNI数据类型的映射关系
Java类型->JNI类型->C类型
- boolean jboolean
- byte jbyte;
- char jchar;
- short jshort;
- int jint;
- long jlong;
- float jfloat;
- double jdouble;
- void void
引用类型(对象)
String jstring
object jobject
数组,基本数据类型的数组
byte[] jByteArray
对象数组
object jobjectArray
函数的实现
C的函数名称
Java_完整类名_函数名
C的函数参数
每个native函数,都至少有两个参数(JNIEnv*,jclass或者jobject)
- 当native方法为静态方法时:jclass 代表native方法所属类的class对象(JniTest.class)
- 当native方法为非静态方法时:jobject 代表native方法所属的对象
访问属性
- GetObjectClass取得对象类
- GetFieldID取属性名称
- GetObjectField取属性值
- SetObjectField设置属性值
访问静态属性
- GetObjectClass取得对象类
- GetStaticFieldID取属性名称
- GetStaticIntField取属性值(以int类型为例)
- SetStaticIntField设置属性值
访问java方法
- GetObjectClass取得对象类
- GetMethodID取方法id(签名规则见下面说明)
- CallIntMethod调用方法
访问静态方法
- GetObjectClass取得对象类
- GetStaticMethodID取方法id(签名规则见下面说明)
- CallStaticObjectMethod调用方法
签名
下表为签名中,类型的对应关系。
Java类型 对应的签名
boolean Z
byte B
char C
shrot S
int I
long L
float F
double D
void V
Object L用/分割包的完整类名; Ljava/lang/String;
Array [签名 [I [Ljava/lang/String;
例:
void set(String str);
签名:"(Ljava/lang/String;)V"
其实除了自己对照手写之外,JDK也提供了一个很好用的生成签名的工具javap,cmd进入控制台到你要生成签名的那个类的目录下。在这里用 Order类打比方,敲入: javap -s -private Order。所有方法签名都会被输出,关于javap的一些参数可以在控制台下面输入 javap -help查看。(做coder的 毕竟还是要认几个单词的)
代码示例
java代码
package com.dm.jni;
import java.util.Random;
import java.util.UUID;
public class JniTest {
public String key = "jason";
public static int count = 9;
public native static String getStringFromC();
public native String getString2FromC(int i);
//访问属性,返回修改之后的属性内容
public native String accessField();
public native void accessStaticField();
public native void accessMethod();
public native void accessStaticMethod();
public static void main(String[] args) {
String text = getStringFromC();
System.out.println(text);
JniTest t = new JniTest();
text = t.getString2FromC(6);
System.out.println(text);
System.out.println("key修改前:"+t.key);
t.accessField();
System.out.println("key修改后:"+t.key);
System.out.println("count修改前:"+count);
t.accessStaticField();
System.out.println("count修改后:"+count);
t.accessMethod();
t.accessStaticMethod();
}
//产生指定范围的随机数
public int genRandomInt(int max){
System.out.println("genRandomInt 执行了...");
return new Random().nextInt(max);
}
//产生UUID字符串
public static String getUUID(){
return UUID.randomUUID().toString();
}
//加载动态库
static{
System.loadLibrary("jni");
}
}
C代码
#define _CRT_SECURE_NO_WARNINGS
#include "com_dm_jni_JniTest.h"
#include
//#include
//函数实现
JNIEXPORT jstring JNICALL Java_com_dm_jni_JniTest_getStringFromC
(JNIEnv *env, jclass jcls){
//JNIEnv 结构体指针
//env二级指针
//代表Java运行环境,调用Java中的代码
//简单的实现
//将C的字符串转为一个java字符串
return (*env)->NewStringUTF(env,"C String");
}
JNIEXPORT jstring JNICALL Java_com_dm_jni_JniTest_getString2FromC
(JNIEnv *env, jobject jobj, jint num){
return (*env)->NewStringUTF(env,"C String2");
}
//C/C++访问Java的成员
//1.访问属性
//修改属性key
JNIEXPORT jstring JNICALL Java_com_dm_jni_JniTest_accessField
(JNIEnv *env, jobject jobj){
//jobj是t对象,JniTest.class
jclass cls = (*env)->GetObjectClass(env, jobj);
//jfieldID
//属性名称,属性签名
jfieldID fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");
//jason >> super jason
//获取key属性的值
//GetField
jstring jstr = (*env)->GetObjectField(env, jobj, fid);
printf("jstr:%#x\n",&jstr);
//jstring -> c字符串
//isCopy 是否复制(true代表赋值,false不复制)
char *c_str = (*env)->GetStringUTFChars(env,jstr,JNI_FALSE);
//拼接得到新的字符串
char text[20] = "super ";
strcat(text,c_str);
//c字符串 ->jstring
jstring new_jstr = (*env)->NewStringUTF(env, text);
//修改key
//SetField
(*env)->SetObjectField(env, jobj, fid, new_jstr);
printf("new_jstr:%#x\n", &new_jstr);
return new_jstr;
}
//访问静态属性
JNIEXPORT void JNICALL Java_com_dm_jni_JniTest_accessStaticField
(JNIEnv *env, jobject jobj){
//jclass
jclass cls = (*env)->GetObjectClass(env, jobj);
//jfieldID
jfieldID fid = (*env)->GetStaticFieldID(env, cls, "count", "I");
//GetStaticField
jint count = (*env)->GetStaticIntField(env, cls, fid);
count++;
//修改
//SetStaticField
(*env)->SetStaticIntField(env,cls,fid,count);
}
//2.访问java方法
JNIEXPORT void JNICALL Java_com_dm_jni_JniTest_accessMethod
(JNIEnv *env, jobject jobj){
//jclass
jclass cls = (*env)->GetObjectClass(env, jobj);
//jmethodID
jmethodID mid = (*env)->GetMethodID(env, cls, "genRandomInt", "(I)I");
//调用
//CallMethod
jint random = (*env)->CallIntMethod(env, jobj, mid, 200);
printf("random num:%ld",random);
//.....
}
//静态方法
JNIEXPORT void JNICALL Java_com_dm_jni_JniTest_accessStaticMethod
(JNIEnv *env, jobject jobj){
//jclass
jclass cls = (*env)->GetObjectClass(env, jobj);
//jmethodID
jmethodID mid = (*env)->GetStaticMethodID(env, cls, "getUUID", "()Ljava/lang/String;");
//调用
//CallStaticMethod
jstring uuid = (*env)->CallStaticObjectMethod(env, cls, mid);
//随机文件名称 uuid.txt
//jstring -> char*
//isCopy JNI_FALSE,代表java和c操作的是同一个字符串
char *uuid_str = (*env)->GetStringUTFChars(env, uuid, JNI_FALSE);
//拼接
char filename[100];
sprintf(filename, "D://%s.txt",uuid_str);
FILE *fp = fopen(filename,"w");
fputs("i love jason", fp);
fclose(fp);
}