一、JNI调用java中的非静态方法
- 1.获取java中声明的函数的签名
1>java中在包名为com.hubin.jin 的Text.java文件中申明了getRandomInt()方法
class Text{
//用来触发JNI执行,演示用的
public native void accessMethod();
int getRandomInt(int max){
return new Random().nextInt(max);
}
}
2>cd到对应的Test.java文件生成的Test.class目录下执行javap命令
3>再执行 javap -s -p Test(Test 为文件名)命令输出签名
- 2.实现C端代码
JNIEXPORT void JNICALL Java_com.hubin.jin_Test_accessMethod
(JNIEnv *env ,jobject jobj){
//1.得到jclass
jclass jclz = (*env)->GetObjectClass(env, jobj);
//2.获取jmethidId
//getRandomInt:java方法的名字
//(I)I:方法的签名
jmethodID mid = (*env)->GetMethodID(env, jclz, "getRandomInt", "(I)I");
//3.调用java getRandomInt()方法 200:getRandomInt的参数
jint random = (*env)->CallIntMethod(env, jobj, mid, 200);
printf("C ramdom:%d\n", random);
}
-
3.动态配置.dll库
假设使用Visual Studio生成.dll库之后生成的路径为: D:\project\JNI_Text\x64\Debug\JNI_text.dll 我们可以将 D:\project\JNI_Text\x64\Debug\ 配置到环境变量 在java工程中就不需要再将.dll库拷贝到libs目录下加载,直接loadLibrary即可: static{ System.loadLibrary("JNI_text"); }
二、JNI调用java中的静态方法
-
1.JVM执行原理
java中访问静态方法:类名.方法名 JVM :ClassLoader(类加载器)加载.class文件 如果加载失败:java.lang.ClassNotFoundException JNI的访问流程跟虚拟机加载的过程很类似
-
2.创建java中的静态方法
class Text{ public native void accessStaticMethod(); static String getRandeomUUId(){ return UUID.randomUUID.toString(); } }
-
3.实现C端代码
//JNI访问java中的静态方法 JNIEXPORT void JNICALL Java_com.hubin.jin_Test_accessStaticMethod (JNIEnv *env ,jobject jobj){ //1.找到jclass //原理:通过jobject来搜索class,如果找到了,将这个class 转变成jclass,然后返回 jclass clz = (*env)->GetObjectClass(env, jobj); //2.找到jmethdId //getRandeomUUId:java方法名字 //()Ljava/lang/String :签名 jmethodID jmid = (*env)->GetStaticMethodID(env, clz, "getRandeomUUId", "()Ljava/lang/String; "); //3.调用静态方法,得到java提供的uuid jstring uuid = (*env)->CallStaticObjectMethod(env, clz, jmid); //jstring转化成char* char * uuid_c = (*env)->GetStringUTFChars(env, uuid, NULL); //生成一个uuid.txt的文本文件到本地 char filename[100]; sprintf(filename, "D://%s.txt", uuid_c); FILE *fp = fopen(filename, "w";);//写入流 fputs("I Love AV", fp); fclose(fp); printf("文件写入成功\n"); }
三、JNI访问java构造方法调用类中的函数
-
1.创建要给触发执行C语言的函数
public class Test{ //触发函数 public native Date accessConstructor(); }
-
2.C端方法实现
JNIEXPORT jobject JNICALL Java_com.hubin.jin_Test_accessConstructor (JNIEnv *env ,jobject jobj){ //1.通过类的路径来从JVM里面找到对应的类 jclass jclz = (*env)->FindClass(env, "java/util/Data"); //2.找到构造函数的jmethodid //
:构造方法的名字,所有的构造方法的名字都是他 //()V :签名 自己用javap命令获取 jmethodID jmid = (*env)->GetMethodID(env, jclz, " ", "()V"); //3.调用newObject实例化一个Date对象,返回值是一个jobject //jni中所有引用的数据类型都会转化成jobject jobject date_obj = (*env)->NewObject(env, jclz, jmid); //4.获取Date类中的getTime方法的jmethidID //前提是,我们访问了相关对象的构造函数创建了这个对象 jmethidID time_mid = (*env)->GetMethodID(env, jclass, "getTime", "()J") //5.执行getTime方法 jlong time =(*env)->CallLongMethod(env, date_obj, time_mid); printf("time: %lld \n", time); return date_obj; }
四、JNI中字符串的转换
- 1.string在C端的转换方式一
1>从java到jni到C/C++,编码的转换过程
//java内部使用的是utf-16 16bit 的编码方式
//jni 里面使用的utf-8 unicode编码方式 英文是1个字节,中文 3个字节
//C/C++ 使用 ascii编码 ,中文的编码方式 GB2312编码 中文 2个字节
2>声明java本地方法
public native String chineseChars(String str);
3> C端代码实现
#include
JNIEXPORT jobject JNICALL Java_com.hubin.jin_Test_chineseChars
(JNIEnv *env ,jobject jobj,jstring in){
jboolean iscp;
char * c_str = (*env)->GetStringChars(env, in, &iscp);
if (iscp == JNI_TRUE){
printf("is copy: JNI_TRUE\n");
}
else if (iscp == JNI_FALSE){
printf("is copy: JNI_FALSE\n");
}
int length = (*env)->GetStringLength(env, in);
const jchar * jcstr = (*env)->GetStringChars(env, in, NULL);
if (jcstr == NULL) {
return NULL;
}
//jchar -> char
char * rtn = (char *)malloc(sizeof(char)* 2 * length + 3);
memset(rtn, 0, sizeof(char)* 2 * length + 3);
int size = 0;
size = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)jcstr, length, rtn, sizeof(char)* 2 * length + 3, NULL, NULL);
/*if (size <= 0)
{
printf("size: 0 \n", rtn);
return NULL;
}*/
if (rtn != NULL) {
free(rtn);
rtn = NULL;
}
(*env)->ReleaseStringChars(env, in, c_str);// JVM 使用。通知JVM c_str 所指的空间可以释放了
printf("string: %s\n", rtn);
return NULL;
}
- 2.string在C端的转换方式二(使用String的构造方法在C端转换java字符串)
1>java中String 有一个构造函数如下
public String(byte bytes[], String charsetName){
this(bytes,0,bytes.length,charsetName);
}
2>C端调用此构造函数生成字符串
JNIEXPORT jobject JNICALL Java_com.hubin.jin_Test_chineseChars
(JNIEnv *env ,jobject jobj,jstring in){
char *c_str = "马蓉与宝宝";
//获取jclass
jclass str_cls = (*env)->FindClass(env, "java/lang/String");
//获取String构造的jmethodID
//([BLjava/lang/String;)V :签名
jmethodID jmid = (*env)->GetMethodID(env, str_cls, "", "([BLjava/lang/String;)V");
//将jstring转换成jbyteArray
jbyteArray bytes = (*env)->NewByteArray(env, strlen(c_str));
//将Char *赋值到bytes
(*env)->SetByteArrayRegion(env, bytes, 0, strlen(c_str), c_str);
jstring charsetName = (*env)->NewStringUTF(env, "GB2312");
return (*env)->NewObject(env, str_cls, jmid, bytes, charsetName);
}