版权声明:本文为博主原创文章,未经博主允许不得转载。
本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。
一、JNI 访问 java 属性值
JNI 调用 C 的动态库,在动态库的方法中进行对 java 属性值的修改,这边分为两类,静态属性和非静态属性。
java 代码:
public class JNIMain {
public native static String getString();
public native String getString2();
private static int count = 0;
public native static void accessStaticField();
private String key = "test";
public native void accessField();
static{
System.loadLibrary("FirstApplication");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(getString());
JNIMain jniMain = new JNIMain();
jniMain.getString2();
System.out.println(jniMain.getString2());
System.out.println("修改前");
System.out.println(count);
System.out.println(jniMain.key);
accessStaticField();
jniMain.accessField();
System.out.println("修改后");
System.out.println(count);
System.out.println(jniMain.key);
}
}
1.修改静态属性值
C 代码:
JNIEXPORT void JNICALL Java_com_xiaoyue_JNIMain_accessStaticField
(JNIEnv *env, jclass jclz) {
//获取 java 类中静态属性的 id
jfieldID fid = (*env)->GetStaticFieldID(env, jclz, "count", "I");
//获取到 java 中的静态属性
jint count = (*env)->GetStaticIntField(env, jclz, fid);
count ++;
//设置 java 中的静态属性
(*env)->SetStaticIntField(env, jclz, fid, count);
}
2.修改非静态属性值
C 代码:
JNIEXPORT void JNICALL Java_com_xiaoyue_JNIMain_accessField
(JNIEnv *env, jobject jobj) {
jclass jclz= (*env)->GetObjectClass(env, jobj);
//获取 java 类中非静态属性的 id
jfieldID fid = (*env)->GetFieldID(env, jclz, "key", "Ljava/lang/String;");
//获取到 java 中的非静态属性
jstring jkey = (*env)->GetObjectField(env, jobj, fid);
// jni -> c
char * key = (*env)->GetStringUTFChars(env, jkey, NULL);
char ch[50] = "change";
strcat(ch, key);
// C -> jni
jstring new_str = (*env)->NewStringUTF(env, ch);
//设置 java 中的静态属性
(*env)->SetObjectField(env, jobj, fid, new_str);
//对旧的字符串进行释放
(*env)->ReleaseStringChars(env, new_str, key);
}
运行结果:
C 语言要对 java 的属性值进行修改,需要用到 JNIEnv,这里有众多的方法可以进行操作。
要操作属性,需要通过 GetStaticFieldID 或 GetFieldID 获取到属性的 id。
//获取 java 类中静态属性的 id
jfieldID fid = (*env)->GetStaticFieldID(env, jclz, "count", "I");
//获取 java 类中非静态属性的 id
jfieldID fid = (*env)->GetFieldID(env, jclz, "key", "Ljava/lang/String;");
第一个参数:为 JNIEnv
第二个参数:为传入类 jclass ,如果没有的话,要通过 (*env)->GetObjectClass(env, jobj) 进行获取
第三个参数:为要获取的属性名
第四个参数:为对应属性的签名,具体看上一篇
在通过 get 方法获取到属性值,修改完成之后进行 set 方法的操作,有具体分静态、非静态、以及属性类型。
二、JNI 访问 java 方法
与 JNI 访问 java 属性类似,都是通过 JNIEnv 来进行实现。
java 代码:
public class JNIMain {
//访问 java 非静态方法
public native void accessMethod();
//访问 java 静态方法
public native void accessStaticMethod();
static{
System.loadLibrary("FirstApplication");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
//JNI 基本流程调用
JNIMain jniMain = new JNIMain();
//JNI 访问 java 方法
System.out.println("JNI 访问 java 方法");
jniMain.accessMethod();
jniMain.accessStaticMethod();
}
int getRandomInt(int max) {
return new Random().nextInt(max);
}
static String getRandeomUUId() {
return UUID.randomUUID().toString();
}
}
1.访问静态方法
C 代码:
JNIEXPORT void JNICALL Java_com_xiaoyue_JNIMain_accessStaticMethod
(JNIEnv *env, jobject jobj) {
jclass jclz= (*env)->GetObjectClass(env, jobj);
//jmethdId,GETStaticMethodID 方法的名字,方法的签名
jmethodID jmid = (*env)->GetStaticMethodID(env, jclz, "getRandeomUUId", "()Ljava/lang/String;");
// 调用静态方法
jstring uuid = (*env)->CallStaticObjectMethod(env, jclz, jmid);
//jstring -> char *
char * uuid_c = (*env)->GetStringUTFChars(env, uuid, NULL);
printf("C UUID: %s \n", uuid_c);
}
2.修改非静态方法
C 代码:
//访问 java 非静态方法
JNIEXPORT void JNICALL Java_com_xiaoyue_JNIMain_accessMethod
(JNIEnv *env, jobject jobj) {
jclass jclz= (*env)->GetObjectClass(env, jobj);
//获取 java 方法的 id
jmethodID methodId = (*env)->GetMethodID(env, jclz, "getRandomInt", "(I)I");
//调用
jint random = (*env)->CallIntMethod(env, jobj, methodId, 200);
printf("C random: %d \n", random);
}
运行结果:
//获取 java 方法的 id
jmethodID methodId = (*env)->GetMethodID(env, jclz, "getRandomInt", "(I)I");
第一个参数:为 JNIEnv
第二个参数:为传入类 jclass ,如果没有的话,要通过 (*env)->GetObjectClass(env, jobj) 进行获取
第三个参数:为要获取的方法名
第四个参数:为对应方法的签名
方法签名的获取:命令行窗口切换到对应类的 .class 文件路径,使用 javap 命令进行查看,熟悉的也可以自己进行获取。
运行结果:
三、JNI 访问 java 构造方法
也是通过 JNIEnv 来进行实现。
java 代码:
package com.xiaoyue;
import java.util.Date;
import java.util.Random;
import java.util.UUID;
public class JNIMain {
//访问 java 构造方法
public native Date acceessConstructor();
static{
System.loadLibrary("FirstApplication");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
JNIMain jniMain = new JNIMain();
//JNI 访问 java 构造方法
System.out.println("JNI 访问 java 构造方法");
jniMain.acceessConstructor();
}
C 代码:
JNIEXPORT jobject JNICALL Java_com_xiaoyue_JNIMain_acceessConstructor
(JNIEnv *env, jobject jobj) {
jclass jclz = (*env)->FindClass(env, "java/util/Date");
//jmethodid
jmethodID jmid = (*env)->GetMethodID(env, jclz, "<init>", "()V");
//调用 newObject 实例化Date 对象,返回值是一个jobjcct
jobject date_obj = (*env)->NewObject(env, jclz, jmid);
//得到对应对象的方法,前提是,我们访问了相关对象的构造函数创建了这个对象
jmethodID time_mid = (*env)->GetMethodID(env, jclz, "getTime", "()J");
jlong time = (*env)->CallLongMethod(env, date_obj, time_mid);
printf("time: %lld \n", time);
return date_obj;
}
jmethodID jmid = (*env)->GetMethodID(env, jclz, "<init>", "()V");
获取构造函数的方法 id ,传入的方法名都是 “”。
运行结果: