JNI数据类型和方法

270 阅读5分钟

上一节中我们学习了JNI的开发流程,这一节我们学习JNI的数据类型。

关于数据类型的映射,大家可以参考这篇文章 JNI学习积累之二 ---- 数据类型映射、域描述符说明

这里直接拿取了文章数据类型的列表

JAVA基本数据类型,可以直接使用,不需要转换。其映射关系:Java类型->JNI类型->C类型

Java基本数据类型与JNI数据类型的映射关系:

基本数据类型.png

上面的列表展示出来就是java的基本数据类型对应JNI的数据类型。

除了基本数据类型,还有JAVA的引用类型(对象),引用数据类型需要转换后才能使用。

引用类型.png

了解native函数

每个native函数,都至少有两个参数(JNIEnv*,jclass或者jobject) 1)当native方法为静态方法时:jclass 代表native方法所属类的class对象(XX.class)

2)当native方法为非静态方法时:jobject 代表native方法所属的对象

public class Test {

	public native static String getStringFromC();
	
	public native String getString2FromC(int i);
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		String text = getStringFromC();
		System.out.println(text);
		//访问非静态方法
		Test t = new Test();
		System.out.println(t.getString2FromC(500));
		
	}
	
	//加载动态库
	static {
		System.loadLibrary("ConsoleApplication1");
	}

}

这里的getStringFromC是静态方法,getString2FromC非静态方法

头文件:

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

#ifndef _Included_com_lwj_test_Test
#define _Included_com_lwj_test_Test
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_lwj_test_Test
 * Method:    getStringFromC
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_lwj_test_Test_getStringFromC
  (JNIEnv *, jclass);
JNIEXPORT jstring JNICALL Java_com_lwj_test_Test_getString2FromC
(JNIEnv *, jclass,jint);

#ifdef __cplusplus
}
#endif
#endif

头文件Java_com_lwj_test_Test_getStringFromC方法中jclass是指:Test.class;

头文件Java_com_lwj_test_Test_getString2FromC方法中jclass是指Test类实例化的t对象。

访问属性方法

修改Java类的属性 Java类:

public class Test {

	public native static String getStringFromC();
	
	public native String getString2FromC(int i);
	
	public String name = "xiaoming";
	//访问属性,返回修改之后的属性内容
	public native String updateName();
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Test t = new Test();
		System.out.print("修改前:"+t.name+"\n");
		t.updateName();
		System.out.print("修改后:"+t.name);
		
	}
	
	//加载动态库
	static {
		System.loadLibrary("ConsoleApplication1");
	}

}

通过native方法的updateName修改属性name

C实现:


JNIEXPORT jstring JNICALL Java_com_lwj_test_Test_updateName
(JNIEnv *env, jobject jobj){
	//得到class
	jclass jcls = (*env)->GetObjectClass(env, jobj);
	//获取jfieldID 需要属性名称,属性签名
	jfieldID fid = (*env)->GetFieldID(env, jcls, "name", "Ljava/lang/String;");
	//获取值
	jstring jstr = (*env)->GetObjectField(env, jobj, fid);
	//jstring->char
	//iscopy:Null,这里只需要传NULL即可,需不需要copy一份,不是我们能控制的,是函数内部控制
	char *str1 = (*env)->GetStringUTFChars(env,jstr,NULL);
	//char *str1 = (*env)->GetStringUTFChars(env, jstr, JNI_FALSE);

	char str2[30] = "my name is ";
	//strcat(str2, str1);
	strcat_s(str2,30,str1);

	//c字符串 ->jstring
	jstring new_jstr = (*env)->NewStringUTF(env, str2);

	//修改name的值,Set<Type>Field
	(*env)->SetObjectField(env, jobj, fid, new_jstr);

	return new_jstr;

}

生成动态库之后复制到Java工程下,运行得到

运行结果.png

属性签名

上面jfieldID fid = (*env)->GetFieldID(env, jcls, "name", "Ljava/lang/String;");需要传入String的属性签名。 关于属性签名可以查看下面的表格:

Java属性与方法签名列表.png

方法签名

1)打开cmd 2)进入Java项目的bin目录 3)使用javap命令: javap 如: javap -s -p com.lwj.test.Test

签名.png

运行结果.png

访问静态属性

Java类

public class Test {

	public native static String getStringFromC();
	
	public native String getString2FromC(int i);
	
	public String name = "xiaoming";
	//访问属性,返回修改之后的属性内容
	public native String updateName();
	
	public static int count = 90;
	//访问静态属性的本地
	public native void accessStaticField();
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Test t = new Test();
		System.out.print("修改前:"+count+"\n");
		t.accessStaticField();
		System.out.print("修改后:"+count);
	
	}
	
	//加载动态库
	static {
		System.loadLibrary("ConsoleApplication1");
	}

}

头文件添加方法

JNIEXPORT jstring JNICALL Java_com_lwj_test_Test_accessStaticField
(JNIEnv *, jobject);

C实现方法

JNIEXPORT jstring JNICALL Java_com_lwj_test_Test_accessStaticField
(JNIEnv *env, jobject jobj){
	//jclass
	jclass jcls = (*env)->GetObjectClass(env, jobj);
	//jfieldID
	jfieldID fid = (*env)->GetStaticFieldID(env, jcls, "count", "I");
	//GetStatic<Type>Field 
	jint count = (*env)->GetStaticIntField(env,jcls,fid);
	count++;
	//SetStatic<Type>Field
	(*env)->SetStaticIntField(env, jcls, fid, count);
}

生成动态库,运行得到结果:

运行结果.png

访问java方法

JAVA 类

public class Test {

	public native static String getStringFromC();
	
	public native String getString2FromC(int i);
	
	public String name = "123";
	//访问属性,返回修改之后的属性内容
	public native String updateName();
	
	public static int count = 90;
	//访问静态属性的本地
	public native void accessStaticField();
	
	public native void accessMethod();
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Test t = new Test();
		t.accessMethod();
	}
	
	/**
	 * 提供给native调用
	 * @return
	 */
	public int count(int a,int b) {
		return a+b;
	}
	
	
	//加载动态库
	static {
		System.loadLibrary("ConsoleApplication1");
	}

}

头文件添加方法

JNIEXPORT jstring JNICALL Java_com_lwj_test_Test_accessMethod
(JNIEnv *, jobject);

实现方法

//访问java方法
JNIEXPORT jstring JNICALL Java_com_lwj_test_Test_accessMethod
(JNIEnv *env, jobject jobj){
	//jclass
	jclass cls = (*env)->GetObjectClass(env,jobj);
	//jmethodID
	jmethodID mid = (*env)->GetMethodID(env,cls,"count","(II)I");

	//调用
	//Call<Type>Method
	jint count = (*env)->CallIntMethod(env, jobj, mid, 100, 200);
	printf("count:%ld", count);
}

这里需要拿到方法的签名,利用上面的方法签名的方法即可拿到签名。

运行结果.png

访问Java静态方法

Java类

public class Test {

	public native static String getStringFromC();
	
	public native String getString2FromC(int i);
	
	public String name = "123";
	//访问属性,返回修改之后的属性内容
	public native String updateName();
	
	public static int count = 90;
	//访问静态属性的本地
	public native void accessStaticField();
	
	public native void accessMethod();
	
	public native void accessStaticMethod();
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Test t = new Test();
		t.accessStaticMethod();
	}
	
	/**
	 * 提供给native调用
	 * @return
	 */
	public int count(int a,int b) {
		return a+b;
	}
	
	/**
	 * 产生UUID字符串
	 * @return
	 */
	public static String getUUID(){
		return UUID.randomUUID().toString();
	}
		
	
	//加载动态库
	static {
		System.loadLibrary("ConsoleApplication1");
	}

}

头文件添加方法

JNIEXPORT jstring JNICALL Java_com_lwj_test_Test_accessStaticMethod
(JNIEnv *, jobject);

实现方法

JNIEXPORT jstring JNICALL Java_com_lwj_test_Test_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("阴天快乐", fp);
	fclose(fp);

	printf("finish");

}

运行结果:

运行结果.png

运行结果.png

打开文件

运行结果.png

以上就是JNI访问Java的数据类型和属性方法