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 Zbyte Bchar Cshrot Sint Ilong Lfloat Fdouble Dvoid VObject 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 <string.h>//#include <Windows.h>//函数实现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.访问属性//修改属性keyJNIEXPORT 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属性的值 //Get<Type>Field 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 //Set<Type>Field (*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"); //GetStatic<Type>Field jint count = (*env)->GetStaticIntField(env, cls, fid); count++; //修改 //SetStatic<Type>Field (*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"); //调用 //Call<Type>Method 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;"); //调用 //CallStatic<Type>Method 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);}