JNI資料型別和方法

鋸齒流沙發表於2018-01-03

上一節中我們學習了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的資料型別和屬性方法

相關文章