JNI訪問Java方法

鋸齒流沙發表於2017-12-26

上一篇我們學習了《JNI資料型別和屬性方法》,瞭解了JNI和Java資料型別的關係,以及JNI訪問JAVA類的一些方法,包括靜態方法,接下來我們繼續學習。

訪問構造方法

JAVA類定義native方法

public class Test {

	
	public native Data accessConstructor();
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Test t = new Test();
		Data mData = t.accessConstructor();
		
		DateFormat dateFormat = new SimpleDateFormat("EE MM-dd-yyyy");
        System.out.println(dateFormat.format(mData));
		
		System.out.println( mData.toString());
	}
	
	
	//載入動態庫
	static {
		System.loadLibrary("ConsoleApplication1");
	}

}
複製程式碼

按照一般的步驟,需要生成com.lwj.test.Test 類的標頭檔案com_lwj_test_Test.h

這裡演示的是:使用java.util.Date產生一個當前時間時間戳,並且將Date物件返回給Java類的accessConstructor方法。

//實現方法
JNIEXPORT jobject JNICALL Java_com_lwj_test_Test_accessConstructor
(JNIEnv *env, jobject jobj){
	//jclass
	jclass cls = (*env)->FindClass(env, "java/util/Date");
	//構造方法jmethodID
	jmethodID mid = (*env)->GetMethodID(env,cls,"<init>","()V");
	//例項化一個Date物件
	jobject obj = (*env)->NewObject(env,cls,mid);
	//呼叫getTime方法
	jmethodID t_mid = (*env)->GetMethodID(env,cls,"getTime","()J");
	jlong time = (*env)->CallLongMethod(env,obj,t_mid);
	printf("time long:%lld",time);
	//jlong -> 字串
	FILE *fp = fopen("D://log.txt", "w");
	char str[50];
	sprintf(str, "%lld", time);//列印到字串中
	fputs(str, fp);//將字串寫入檔案
	fclose(fp);

	return obj;

}
複製程式碼

生成動態庫(.dll)檔案,之後在Java中呼叫得到結果

執行結果.png

呼叫父類的方法

Java類:

public class Animal {
	
	public void walk() {
		System.out.println("動物都會走路~");
	}

}
複製程式碼
public class Monkey extends Animal{
	
	@Override
	public void walk() {
		// TODO Auto-generated method stub
//		super.walk();
		System.out.println("猴子會爬樹~");
	}
}
複製程式碼
public class Test {

	private Animal mMonkey = new Monkey();
	public native void callSuperMethod();
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Test t = new Test();
		t.callSuperMethod();
	}
		
	
	//載入動態庫
	static {
		System.loadLibrary("ConsoleApplication1");
	}

}
複製程式碼

JNI呼叫

JNIEXPORT void JNICALL Java_com_lwj_test_Test_callSuperMethod
(JNIEnv *env, jobject jobj){
	
	jclass jcls = (*env)->GetObjectClass(env,jobj);

	jfieldID fid = (*env)->GetFieldID(env,jcls,"mMonkey"," Lcom/lwj/test/Animal;");

	jobject super_obj = (*env)->GetObjectField(env, jobj, fid);

	//執行walk方法
	jclass super_cls = (*env)->FindClass(env,"com/lwj/test/Animal");//注意:傳父類的類名
	jmethodID mid = (*env)->GetMethodID(env, super_cls, "walk", "()V");

	//執行子類覆蓋的方法
	//(*env)->CallObjectMethod(env, super_obj, mid);
	//執行父類的方法

	(*env)->CallNonvirtualVoidMethod(env, super_obj, super_cls, mid);

}
複製程式碼

執行結果.png

解決中文字串亂碼

C字串->JSTRING
一般的從JNI返回中文字串會出現亂碼的情況

public class Test {

	
	public native String chineseChars(String text);
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Test t = new Test();
		System.out.println(t.chineseChars("是時候了"));
	}
		
	
	//載入動態庫
	static {
		System.loadLibrary("ConsoleApplication1");
	}

}

複製程式碼

編寫native方法

JNIEXPORT jstring JNICALL Java_com_lwj_test_Test_chineseChars
(JNIEnv *env, jobject jobj, jstring jstr){

	char *cstr = "我叫小明";
	return (*env)->NewStringUTF(env,cstr);
}

複製程式碼

執行結果.png

以上的JNI實現就是直接返回一個jstring,出現了亂碼。

針對這個情況,在實現native函式中進行處理

JNIEXPORT jstring JNICALL Java_com_lwj_test_Test_chineseChars
(JNIEnv *env, jobject jobj, jstring jstr){

	char *cstr = "我叫小明";

	//String類的jclass
	jclass str_cls = (*env)->FindClass(env, "java/lang/String");
	jmethodID constructor_mid = (*env)->GetMethodID(env, str_cls, "<init>", "([BLjava/lang/String;)V");

	//char * -> char[] ->jbyteArray -> jbyte * 
	jbyteArray bytes = (*env)->NewByteArray(env, strlen(cstr));
	//bytes陣列賦值
	(*env)->SetByteArrayRegion(env, bytes, 0, strlen(cstr), cstr);

	//charsetName="GB2312"
	jstring charsetName = (*env)->NewStringUTF(env, "GB2312");

	//返回GB2312中文編碼jstring
	return (*env)->NewObject(env, str_cls, constructor_mid, bytes, charsetName);
}
複製程式碼

這裡借用Java的轉碼API,返回GB2312中文編碼字串,使用java.lang.string的建構函式public String(byte bytes[], String charsetName)完成轉碼。

執行這個構造方法需要三個東西 1))jmethodID 2)byte陣列(jbyteArray)引數 3)charsetName引數(jstring)

最後執行的結果如下:

執行結果.png

陣列處理

演示:傳入int陣列,對陣列進行排序

Java類

public class Test {

	
	public native void giveArray(int[] array);
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] array = {89,100,34,2,10,5};
		Test t = new Test();
		t.giveArray(array);
		for (int i : array) {
			System.out.println(i);
		}
	}
		
	
	//載入動態庫
	static {
		System.loadLibrary("ConsoleApplication1");
	}

}
複製程式碼

實現native方法


int compare(int *a, int *b){
	return (*b) - (*a);
}

JNIEXPORT jstring JNICALL Java_com_lwj_test_Test_giveArray
(JNIEnv * env, jobject obj, jintArray arr){
	//Java的int陣列(jintArray)->C int陣列
	int *elems = (*env)->GetIntArrayElements(env,arr,NULL);
	//陣列的長度
	int len = (*env)->GetArrayLength(env,arr);
	//對(jint)long陣列進行
	qsort(elems, len, sizeof(jint), compare);

	//同步
	//釋放陣列的元素
	//mode引數
	//0,Java陣列進行更新,並且釋放C/C++陣列
	//JNI_ABORT,Java陣列不進行更新,但是釋放C/C++陣列
	//JNI_COMMIT,Java陣列進行更新,不釋放C/C++陣列(函式執行完,陣列還是會釋放)
	(*env)->ReleaseIntArrayElements(env, arr, elems, JNI_COMMIT);
}

複製程式碼

說明:對於java傳值到Jni實現函式 1、如果是java的基本資料型別,傳過來的就是值 2、如果是java的引用型別(物件),傳的就是引用 在這裡 jintArray 是引用型別,是指向Java陣列的指標。

最後需要使用ReleaseIntArrayElementsarr 和 elems 進行同步。

返回指定大小的陣列

public class Test {

	
	public native int[] getArray(int size);
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Test t = new Test();
		int[] array = t.getArray(10);
		for (int i : array) {
			System.out.println(i);
		}
	}
		
	
	//載入動態庫
	static {
		System.loadLibrary("ConsoleApplication1");
	}

}

複製程式碼

實現native方法

JNIEXPORT jstring JNICALL Java_com_lwj_test_Test_getArray
(JNIEnv * env, jobject obj, jint size){
	jintArray arrs = (*env)->NewIntArray(env,size);
	//賦值
	//jintArray -> jint *
	jint *elems = (*env)->GetIntArrayElements(env,arrs,NULL);
	int i = 0;
	//迴圈賦值
	for (; i < size; i++){
		elems[i] = i;
	}
	//同步
	(*env)->ReleaseIntArrayElements(env, arrs, elems, 0);
	return arrs;
}
複製程式碼

執行結果.png

相關文章