JNI语法之数据类型和C访问Java属性和方法

时间:2020-12-23 20:17:30

Java属性与方法签名列表:

JNI语法之数据类型和C访问Java属性和方法

java的方法签名可以用javap -s命令来拿到,进入到java项目的bin目录然后执行命令即可,如图所示:

JNI语法之数据类型和C访问Java属性和方法

java文件:

package com.tz.jni;

import java.util.Random;
import java.util.UUID;

public class Test {

public String name = "Dream";

public static int age = 18;

//获取字符串
public native String getTextString();
//更新属性值
public native String updateName();
//更新年龄
public native void updateAge();

public native int getNumber();

public native void executeRandomUUID();

public static void main(String[] args) {

Test test = new Test();
System.out.println(test.getTextString());
//一旦调用了该方法,在C中更新name
test.updateName();
System.out.println("更新后的值:"+test.name);
test.updateAge();
System.out.println("更新后的age值:"+age);

System.out.println("得到一个数:"+test.getNumber());

test.executeRandomUUID();
}

/**
* 生成一个随机数
* @param max
* @return
*/
public int getRandomInt(int max){
Random rd = new Random();
return rd.nextInt(max);
}

/**
* 生成一个随机字符串
* @return
*/
public static String getUUID(){
return UUID.randomUUID().toString();
}

//加载库
static{
//加载动态库
//补充一下:
//Windows系统下:.dll
//Linux环境下:.so(Android)
//今天加载dll动态库
System.loadLibrary("NDK_JNI_JAVA_TEST");
}

}



头文件:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_tz_jni_Test */

#ifndef _Included_com_tz_jni_Test
#define _Included_com_tz_jni_Test
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_tz_jni_Test
* Method: getTextString
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_tz_jni_Test_getTextString
(JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

c文件:

#define _CRT_SECURE_NO_WARNINGS
//注意:导入系统的头文件<>,导入自己头文件""
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "com_tz_jni_Test.h"

//内容一:在windows下,通过JNI调用dll动态库的实现
//第一步:定义native方法
//第二步:通过javah命令生成.h头文件
//第三步:将生成.h头文件复制到C项目当中(vs)
//第四步:导入jni支持头文件(将jni.h、jni_md.h复制到C项目中,以上两个文件在jdk安装目录下)
//第五步:实现native方法(实际上实在c中实现)
//第六步:配置属性,生成dll动态库
//第七步:配置dll动态库所在的目录(配置环境变量:目的为了在java中能够调用到)
//第八步:重启eclipse(目的:为了让eclipse重新读取系统环境变量配置)
//第九步:启动运行

//补充:如果你不想这么麻烦每一次都去配置环境变量,那么你就固定配置一个环境变量目录
//之后的这些dll动态库,拷贝下去就好了

//内容二:分析JNI方法

//原始写法(一般写法)
//jstring Java_com_tz_jni_Test_getTextString
//(JNIEnv *env, jobject jobj){
//return (*env)->NewStringUTF(env, "Hello Jni!");
//}
//1.1 分析JNIEXPORT:宏定义(作用:dll动态库中允许该函数被外部调用)
//为什么要这些?行业规范(编译动态库规范)--固定格式
//1.2 分析JNICALL:用于约束函数入栈顺序和堆栈清理规则(注意:windows下的规则__stdcall)
//如果删除JNIEXPORT和JNICALL也不会报错,系统编译时候自动加上
//1.3 分析方法名称含义:Java_com_tz_jni_Test_getTextString
//一种命名规范(区分方法)(说白了就是唯一标识)
//1.4 分析JNIEnv是什么?干了什么事情?
//JNIEnv是一个结构体指针(指针也是变量,也可以存储指针地址)
//目的:通过JNIEnv访问Java中的方法,属性,创建对象,加载类等等......
//1.5 分析jobject(代表了什么?什么含义)
//两张情况(jobject有两张含义)
//第一种情况:如果你的native方法不是静态方法,那么jobject代表该方法对应的java对象
//(例如:说白了就是我们java中调用的test对象)
//第二种情况:如果你的native方法是静态方法,那么jobject代表该方法对应的class对象
JNIEXPORT jstring JNICALL Java_com_tz_jni_Test_getTextString
(JNIEnv *env, jobject jobj){
return (*env)->NewStringUTF(env, "Hello Jni!");
}

//jni中基本数据类型
//java jni(中介) C
//boolean jboolean char
//int jint int
//double jdouble double
//以此类推基本数据类型对应关系

//引用类型
//java jni(中介) C
//String jstring char字符数组
//Object jobject ....
//注意:基本数据类型数组也是引用类型
//byte[] jbyteArray ....


//C访问Java成员(访问实例属性)
JNIEXPORT jstring JNICALL Java_com_tz_jni_Test_updateName
(JNIEnv *env, jobject jobj){
//在C中不能够通过实例调用属性(说白了就是反射)
//class对象(类对象)
jclass cls = (*env)->GetObjectClass(env, jobj);
//获取属性(java中称之为属性对象)
//参数一:JNIEnv *env
//参数二:class对象
//参数三:属性名称
//参数四:属性签名(说白了就是去指定类型)
//属性签名就是类型的简写
jfieldID fid = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;");
//获取属性的值(类似java反射中)
//注意:Get<签名类型>Field
//参数一:JNIEnv *env
//参数二:哪一个对象--jobj
//参数三:哪一个属性--fid
//返回值:就是我们的jstring(在java中所有的类都是object子类)
//在jni中也是,所有的都是jobject的子类
jstring jstr = (*env)->GetObjectField(env, jobj, fid);

//将jni的类型转成C/C++中的数据类型
char* str = (*env)->GetStringUTFChars(env, jstr, NULL);

//更新str
char newStr[] = "Jackey";
strcat(newStr, str);

//又要将C/C++数据类型转成jni类型
jstr = (*env)->NewStringUTF(env,newStr);

//更新java对象中的属性的值
(*env)->SetObjectField(env, jobj, fid, jstr);

return jstr;
}

//C/C++访问Java中的静态属性
JNIEXPORT void JNICALL Java_com_tz_jni_Test_updateAge
(JNIEnv *env, jobject jobj){
//在C中不能够通过实例调用属性(说白了就是反射)
//class对象(类对象)
jclass cls = (*env)->GetObjectClass(env, jobj);
//获取属性
jfieldID fid = (*env)->GetStaticFieldID(env, cls, "age", "I");
//获取值
//注意:GetStatic<类型>Field
jint age = (*env)->GetStaticIntField(env, cls, fid);
//jint就是long类型,可以直接操作
age += 50;
//更新java静态属性值
//注意:SetStatic<类型>Field
(*env)->SetStaticIntField(env, cls, fid, age);
}

//C/C++访问java实例方法(学习如何调用)
//小案例:生成随机数
JNIEXPORT jint JNICALL Java_com_tz_jni_Test_getNumber
(JNIEnv *env, jobject jobj){
//在C中不能够通过实例调用属性(说白了就是反射)
//class对象(类对象)
jclass cls = (*env)->GetObjectClass(env, jobj);

//获取实例方法
//javap -s -p 类名
jmethodID mid = (*env)->GetMethodID(env, cls, "getRandomInt","(I)I");
//调用方法(执行方法)类似于java中的invoke
//参数一:env
//参数二:实例对象
//参数三:调用哪一个方法
//参数四:传入的参数列表
jint randomInt = (*env)->CallIntMethod(env, jobj, mid, 100);


return randomInt;
}


//C/C++访问java静态方法
//javap命令,能够获取签名
JNIEXPORT void JNICALL Java_com_tz_jni_Test_executeRandomUUID
(JNIEnv *env, jobject jobj){
//在C中不能够通过实例调用属性(说白了就是反射)
//class对象(类对象) Class<?> clz
jclass cls = (*env)->GetObjectClass(env, jobj);
//获取静态方法
jmethodID mid = (*env)->GetStaticMethodID(env, cls, "getUUID", "()Ljava/lang/String;");
//执行方法
jstring jstr = (*env)->CallStaticObjectMethod(env, cls, mid);

//将jstring转成c的字符串
char* uuid = (*env)->GetStringUTFChars(env, jstr, NULL);
//可以将值写到一个文件中
char path[100];
sprintf(path, "F://%s.txt", uuid);

//将uuid写入文件中
FILE*f = fopen(path, "w");
fputs(uuid, f);
fclose(f);
}



整理自示例代码