當我們在呼叫一個Java native方法的時候,方法中的引數是如何傳遞給C/C++本地函式中的呢?Java方法中的引數與C/C++函式中的引數,它們之間是怎麼轉換的呢?我猜你應該也有相關的疑慮吧,我們們先來看一個例子,還是以HelloWorld為例:
HelloWorld.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package com.study.jnilearn; class MyClass {} public class HelloWorld { public static native void test(short s, int i, long l, float f, double d, char c, boolean z, byte b, String str, Object obj, MyClass p, int[] arr); public static void main(String[] args) { String obj = "obj"; short s = 1; long l = 20; byte b = 127; test(s, 1, l, 1.0f, 10.5, 'A', true, b, "中國", obj, new MyClass(), new int[] {}); } static { System.loadLibrary("HelloWorld"); } } |
在HelloWorld.java中定義了一個test的native方法,該方法中一個共有12個引數,其中前面8個為基本資料型別,後面4個全部為引用型別。
由HelloWorld.class生成的native函式原型及實現:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/* * Class: com_study_jnilearn_HelloWorld * Method: test * Signature: (SIJFDCZBLjava/lang/String;Ljava/lang/Object;Lcom/study/jnilearn/MyClass;[I)V */ JNIEXPORT void JNICALL Java_com_study_jnilearn_HelloWorld_test (JNIEnv *env, jclass cls, jshort s, jint i, jlong l, jfloat f, jdouble d, jchar c, jboolean z, jbyte b, jstring j_str, jobject jobj1, jobject job2, jintArray j_int_arr) { printf("s=%hd, i=%d, l=%ld, f=%f, d=%lf, c=%c, z=%c, b=%d", s, i, l, f, d, c, z, b); const char *c_str = NULL; c_str = (*env)->GetStringUTFChars(env, j_str, NULL); if (c_str == NULL) { return; // memory out } (*env)->ReleaseStringUTFChars(env, j_str, c_str); printf("c_str: %s\n", (char*)c_str); } |
呼叫test方法的輸出結果:
從標頭檔案函式的原型可以得知,test方法中形參的資料型別自動轉換成了JNI中相應的資料型別,不難理解,在呼叫Java native方法將實參傳遞給C/C++函式的時候,會自動將java形參的資料型別自動轉換成C/C++相應的資料型別,所以我們在寫JNI程式的時候,必須要明白它們之間資料型別的對應關係。
在Java語言中資料型別分為基本資料型別和引用型別,其中基本資料型別有8種:byte、char、short、int、long、float、double、boolean,除了基本資料型別外其它都是引用型別:Object、String、陣列等。8種基本資料型別分別對應JNI資料型別中的jbyte、jchar、jshort、jint、jlong、jfloat、jdouble、jboolean。所有的JNI引用型別全部是jobject型別,為了使用方便和型別安全,JNI定義了一個引用型別集合,集合當中的所有型別都是jobject的子類,這些子類和Java中常用的引用型別相對應。例如:jstring表示字串、jclass表示class位元組碼物件、jthrowable表示異常、jarray表示陣列,另外jarray派生了8個子類,分別對應Java中的8種基本資料型別(jintArray、jshortArray、jlongArray等)。下面再回顧頭來看看test方法與Java_com_study_jnilearn_HelloWorld_test函式中引數型別的對應關係:
1 2 3 4 5 6 7 |
// HelloWorld.java public static native void test(short s, int i, long l, float f, double d, char c, boolean z, byte b, String str, Object obj, MyClass p); // HelloWorld.h JNIEXPORT void JNICALL Java_com_study_jnilearn_HelloWorld_test (JNIEnv *, jclass, jshort, jint, jlong, jfloat, jdouble, jchar, jboolean, jbyte, jstring, jobject, jobject, jintArray); |
從上面兩個函式的引數中可以看出來,除了JNIEnv和jclass這兩個引數外,其它引數都是一一對應的。下面是JNI規範文件中描述Java與JNI資料型別的對應關係:
基本資料型別:
引用型別:
注意:
1、JNI如果使用C++語言編寫的話,所有引用型別派生自jobject,使用C++的繼承結構特性,使用相應的型別。如下所示:
1 2 3 4 5 6 7 |
class _jobject {}; class _jclass : public _jobject {}; class _jstring : public _jobject {}; class _jarray : public _jobject {}; class _jbooleanArray : public _jarray {}; class _jbyteArray : public _jarray {}; ... |
2、JNI如果使用C語言編寫的話,所有引用型別使用jobject,其它引用型別使用typedef重新定義,如:typedef jobject jstring
jvalue型別:
jvalue是一個unio(聯合)型別,在C語中為了節約記憶體,會用聯合型別變數來儲存宣告在聯合體中的任意型別資料 。在JNI中將基本資料型別與引用型別定義在一個聯合型別中,表示用jvalue定義的變數,可以儲存任意JNI型別的資料,後面會介紹jvalue在JNI程式設計當中的應用。原型如下:
1 2 3 4 5 6 7 8 9 10 11 |
typedef union jvalue { jboolean z; jbyte b; jchar c; jshort s; jint i; jlong j; jfloat f; jdouble d; jobject l; } jvalue; |
如果對unio型別不太明白的同學,請參考相關資料,在這裡不細講。