JNI初步入門后,在傳遞數據的時候,遇到一個需求:有多個數據需要在Java與C代碼之間進行傳遞。如果都做為函數參數傳入,則函數很長很難看,並且多個數據的返回也不好實現。所以想到了把數據打包后傳遞。這在C語言中就是結構體,在Java中就是類了。
我們要做的工作就是,先確定要傳遞的數據,然后相應在C與Java中定義相應的數據類型,然后通過JNI進行數據對應。下面以一個例程來逐步說明。
為了更好的說明各種數據類型的轉換示例,我們的數據包含整型、字符串、浮點數、字符、布爾值、數組。
在Java端定義類:
public class ParamInfo { public boolean boolValue; public char charValue; public double doubleValue; public int intValue; public byte[] array; public String str; }
在C端定義結構體:
typedef struct{ bool boolValue; char charValue; double doubleValue; int intValue; char array[255]; char str[255]; }ParamInfo;
jni接口中並不要求兩邊的變量名一致,或者類名與結構體名一致,只是我們為了邏輯清晰,最好將名稱定義的一致,以便於在后續編寫代碼的過程中更好的將相應數據一一對應起來。
在C代碼中獲取Java代碼傳遞的參數:
- 以獲取類中一個整型值為例:
//獲取Java中的實例類ParamInfo
jclass jcInfo = env->FindClass("com/example/helloworld/ParamInfo");
- 1
其中,com/example/helloworld 是包名對應路徑,ParamInfo是包含數據接口的類名。
- 獲取類中一個整型變量intValue的定義
jfieldID jfi = env->GetFieldID(jcInfo, "intValue", "I");
- 1
- 獲取實例的變量intValue的值,其中jobj即參數中攜帶數據的對象:
paramInfo.intValue = env->GetIntField(jobj, jfi);
- 1
在C代碼中設置向Java端傳遞的參數:
以傳遞結構體中一個整型值為例:
- 先設置結構體中整型值:
paramInfo.intValue = 8;
- 1
- 獲取Java中的實例類ParamInfo
jclass jcInfo = env->FindClass("com/example/helloworld/ParamInfo");
- 1
其中com/example/helloworld 是包名對應路徑,ParamInfo是包含數據接口的類名。
- 獲取類中一個整型變量intValue的定義
jfieldID jfi = env->GetFieldID(jcInfo, "intValue", "I");
- 1
- 創建新的對象
jobject joInfo = env->AllocObject(jcInfo);
- 1
- 設置實例的變量intValue的值
env->SetIntField(joInfo, jfi, paramInfo.intValue);
- 1
- 最后返回該對象
return joInfo;
- 1
其余數據類型值的訪問,都是類似的步驟。注意 GetFieldID()的第3個參數的取值,是數據類型的簽名標識,具體取值見下面表格:
請查看下表:
Java類型 | 符號 |
---|---|
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | L |
float | F |
double | D |
void | V |
object對象 | LClassName; L類名; |
Arrays | [array-type [數組類型 |
methods方法 | (argument-types)return-type (參數類型)返回類型 |
native代碼
知道這些了,就可以進行我們native代碼的書寫了,如下:
// Java 類向C結構體類型轉換 JNIEXPORT jint JNICALL Java_com_example_helloworld_JniClient_setInfo (JNIEnv *env, jobject jo, jobject jobj) { ParamInfo paramInfo; //獲取Java中的實例類ParamInfo jclass jcInfo = env->FindClass("com/example/helloworld/ParamInfo"); //獲取類中每一個變量的定義 //boolean boolValue jfieldID jfb = env->GetFieldID(jcInfo, "boolValue", "Z"); //char charValue jfieldID jfc = env->GetFieldID(jcInfo, "charValue", "C"); //double charValue jfieldID jfd = env->GetFieldID(jcInfo, "doubleValue", "D"); //int intValue jfieldID jfi = env->GetFieldID(jcInfo, "intValue", "I"); //byte[] array jfieldID jfa = env->GetFieldID(jcInfo, "array", "[B"); //String str jfieldID jfs = env->GetFieldID(jcInfo, "str", "Ljava/lang/String;"); //獲取實例的變量boolValue的值 paramInfo.boolValue = env->GetBooleanField(jobj, jfb); //獲取實例的變量charValue的值 paramInfo.charValue = env->GetCharField(jobj, jfc); //獲取實例的變量doubleValue的值 paramInfo.doubleValue = env->GetDoubleField(jobj, jfd); //獲取實例的變量intValue的值 paramInfo.intValue = env->GetIntField(jobj, jfi); //獲取實例的變量array的值 jbyteArray ja = (jbyteArray)env->GetObjectField(jobj, jfa); int nArrLen = env->GetArrayLength(ja); char *chArr = (char*)env->GetByteArrayElements(ja, 0); memcpy(paramInfo.array, chArr, nArrLen); //獲取實例的變量str的值 jstring jstr = (jstring)env->GetObjectField(jobj, jfs); const char* pszStr = (char*)env->GetStringUTFChars(jstr, 0); strcpy(paramInfo.str, pszStr); //日志輸出 LOGI("paramInfo.array=%s, paramInfo.boolValue=%d, paramInfo.charValue=%c\n", paramInfo.array, paramInfo.boolValue, paramInfo.charValue); LOGI("paramInfo.doubleValue=%lf, paramInfo.intValue=%d, paramInfo.str=%s\n", paramInfo.doubleValue, paramInfo.intValue, paramInfo.str); return 0; } // C結構體類型向Java 類轉換 JNIEXPORT jobject JNICALL Java_com_example_helloworld_JniClient_getInfo (JNIEnv *env, jobject jo) { char chTmp[] = "Test array"; int nTmpLen = strlen(chTmp); //將C結構體轉換成Java類 ParamInfo paramInfo; memset(paramInfo.array, 0, sizeof(paramInfo.array)); memcpy(paramInfo.array, chTmp, strlen(chTmp)); paramInfo.boolValue = true; paramInfo.charValue = 'B'; paramInfo.doubleValue = 2.7182; paramInfo.intValue = 8; strcpy(paramInfo.str, "Hello from JNI"); LOGI("paramInfo.array=%s, paramInfo.boolValue=%d, paramInfo.charValue=%c\n", paramInfo.array, paramInfo.boolValue, paramInfo.charValue); //獲取Java中的實例類 jclass jcInfo = env->FindClass("com/example/helloworld/ParamInfo"); //獲取類中每一個變量的定義 //boolean boolValue jfieldID jfb = env->GetFieldID(jcInfo, "boolValue", "Z"); //char charValue jfieldID jfc = env->GetFieldID(jcInfo, "charValue", "C"); //double doubleValue jfieldID jfd = env->GetFieldID(jcInfo, "doubleValue", "D"); //int intValue jfieldID jfi = env->GetFieldID(jcInfo, "intValue", "I"); //byte[] array jfieldID jfa = env->GetFieldID(jcInfo, "array", "[B"); //String str jfieldID jfs = env->GetFieldID(jcInfo, "str", "Ljava/lang/String;"); //創建新的對象 jobject joInfo = env->AllocObject(jcInfo); //給類成員賦值 env->SetBooleanField(joInfo, jfb, paramInfo.boolValue); env->SetCharField(joInfo, jfc, (jchar)paramInfo.charValue); env->SetDoubleField(joInfo, jfd, paramInfo.doubleValue); env->SetIntField(joInfo, jfi, paramInfo.intValue); //數組賦值 jbyteArray jarr = env->NewByteArray(nTmpLen); jbyte *jby = env->GetByteArrayElements(jarr, 0); memcpy(jby, paramInfo.array, nTmpLen); env->SetByteArrayRegion(jarr, 0, nTmpLen, jby); env->SetObjectField(joInfo, jfa, jarr); //字符串賦值 jstring jstrTmp = env->NewStringUTF(paramInfo.str); env->SetObjectField(joInfo, jfs, jstrTmp); return joInfo; }
Java端測試代碼:
// 動態加載C庫
System.loadLibrary("HelloWorld"); //進行對象的jni傳遞 ParamInfo paramInfoSet = new ParamInfo(); byte[] b = new byte[10]; for (int i = 0; i < 9; i++) { b[i] = (byte) (i + 97); } paramInfoSet.array = b; paramInfoSet.boolValue = false; paramInfoSet.charValue = 'C'; paramInfoSet.doubleValue = 3.14; paramInfoSet.intValue = 2016; paramInfoSet.str = "Hello from Java"; Log.i("Hello", "log: to access lib"); JniClient.setInfo(paramInfoSet); Log.i("Hello", "log: after setInfo"); //進行對象的jni接收 ParamInfo paramInfoGet = JniClient.getInfo(); Log.i("Hello", "log: paramInfoGet.boolValue=" + paramInfoGet.boolValue + " paramInfoGet.charValue=" + paramInfoGet.charValue + " paramInfoGet.doubleValue=" + paramInfoGet.doubleValue); Log.i("Hello", "log: paramInfoGet.intValue=" + paramInfoGet.intValue + " paramInfoGet.array=" + new String (paramInfoGet.array) + " paramInfoGet.str=" + paramInfoGet.str); //將收到的字符串顯示到界面上 TextView tv_say_hello = (TextView) findViewById(R.id.tv_say_hello); tv_say_hello.setText(paramInfoGet.str); Log.i("Hello", "log: finish");
- 1
最后輸出的日志信息:
06-25 17:04:25.740: I/Hello(19039): log: to access lib 06-25 17:04:25.740: I/logfromc(19039): paramInfo.array=abcdefghi, paramInfo.boolValue=0, paramInfo.charValue=C 06-25 17:04:25.740: I/logfromc(19039): paramInfo.doubleValue=3.140000, paramInfo.intValue=2016, paramInfo.str=Hello from Java 06-25 17:04:25.740: I/Hello(19039): log: after setInfo 06-25 17:04:25.740: I/logfromc(19039): paramInfo.array=Test array, paramInfo.boolValue=1, paramInfo.charValue=B 06-25 17:04:25.740: I/Hello(19039): log: paramInfoGet.boolValue=true paramInfoGet.charValue=B paramInfoGet.doubleValue=2.7182 06-25 17:04:25.740: I/Hello(19039): log: paramInfoGet.intValue=8 paramInfoGet.array=Test array paramInfoGet.str=Hello from JNI 06-25 17:04:25.740: I/Hello(19039): log: finish
可以看出,java設置的數值,已經成功傳遞到C端;而C程序設置的數據,也成功回傳到java端了。