Jni中C++和Java的參數(shù)傳遞
如何使用JNI的一些基本方法和過(guò)程在網(wǎng)上多如牛毛,如果你對(duì)Jni不甚了解,不知道Jni是做什么的,如何建立一個(gè)基本的jni程序,或許可以參考下面下面這些文章: 如果你習(xí)慣了使用JNI,你就不會(huì)覺得它難了。既然本地方法是由其他語(yǔ)言實(shí)現(xiàn)的,它們?cè)贘ava中沒有函數(shù)體。但是,所有本地代碼必須用本地關(guān)鍵詞聲明,成為Java類的成員。假設(shè)我們?cè)贑++中有這么一個(gè)結(jié)構(gòu),它用來(lái)描述硬盤信息: // 硬盤信息 struct { char name[256]; int serial; }DiskInfo; 那么我們需要在Java中定義一個(gè)類來(lái)與之匹配,聲明可以寫成這樣: class DiskInfo { // 名字 public String name; // 序列號(hào) public int serial; } 在這個(gè)類中,申明一些Native的本地方法,來(lái)測(cè)試方法參數(shù)的傳遞,分別定義了一些函數(shù),用來(lái)傳遞結(jié)構(gòu)或者結(jié)構(gòu)數(shù)組,具體定義如下面代碼: /**//****************** 定義本地方法 ********************/ // 輸入常用的數(shù)值類型(Boolean,Byte,Char,Short,Int,Float,Double) public native void displayParms(String showText, int i, boolean bl); // 調(diào)用一個(gè)靜態(tài)方法 public native int add( int a, int b); // 輸入一個(gè)數(shù)組 public native void setArray(boolean[] blList); // 返回一個(gè)字符串?dāng)?shù)組 public native String[] getStringArray(); // 返回一個(gè)結(jié)構(gòu) public native DiskInfo getStruct(); // 返回一個(gè)結(jié)構(gòu)數(shù)組 public native DiskInfo[] getStructArray(); 編譯生成C/C++頭文件 返回值和參數(shù)類型根據(jù)等價(jià)約定映射到本地C/C++類型,如表JNI類型映射所示。有些類型,在本地代碼中可直接使用,而其他類型只有通過(guò)JNI調(diào)用操作。 表A
※ JNI類型映射 JNI通過(guò)JNIEnv提供的操作Java數(shù)組的功能。它提供了兩個(gè)函數(shù):一個(gè)是操作java的簡(jiǎn)單型數(shù)組的,另一個(gè)是操作對(duì)象類型數(shù)組的。 因?yàn)樗俣鹊脑颍?jiǎn)單類型的數(shù)組作為指向本地類型的指針暴露給本地代碼。因此,它們能作為常規(guī)的數(shù)組存取。這個(gè)指針是指向?qū)嶋H的Java數(shù)組或者Java數(shù)組的拷貝的指針。另外,數(shù)組的布置保證匹配本地類型。 為了存取Java簡(jiǎn)單類型的數(shù)組,你就要要使用GetXXXArrayElements函數(shù)(見表B),XXX代表了數(shù)組的類型。這個(gè)函數(shù)把Java數(shù)組看成參數(shù),返回一個(gè)指向?qū)?yīng)的本地類型的數(shù)組的指針。 表B
JNI數(shù)組存取函數(shù) 當(dāng)你對(duì)數(shù)組的存取完成后,要確保調(diào)用相應(yīng)的ReleaseXXXArrayElements函數(shù),參數(shù)是對(duì)應(yīng)Java數(shù)組和GetXXXArrayElements返回的指針。如果必要的話,這個(gè)釋放函數(shù)會(huì)復(fù)制你做的任何變化(這樣它們就反射到j(luò)ava數(shù)組),然后釋放所有相關(guān)的資源。 為了使用java對(duì)象的數(shù)組,你必須使用GetObjectArrayElement函數(shù)和SetObjectArrayElement函數(shù),分別去get,set數(shù)組的元素。GetArrayLength函數(shù)會(huì)返回?cái)?shù)組的長(zhǎng)度。 JNI 提供的另外一個(gè)功能是在本地代碼中使用Java對(duì)象。通過(guò)使用合適的JNI函數(shù),你可以創(chuàng)建Java對(duì)象,get、set 靜態(tài)(static)和實(shí)例(instance)的域,調(diào)用靜態(tài)(static)和實(shí)例(instance)函數(shù)。JNI通過(guò)ID識(shí)別域和方法,一個(gè)域或 方法的ID是任何處理域和方法的函數(shù)的必須參數(shù)。 表C列出了用以得到靜態(tài)(static)和實(shí)例(instance)的域與方法的JNI函數(shù)。每個(gè)函數(shù)接受(作為參數(shù))域或方法的類,它們的名稱,符號(hào)和它們對(duì)應(yīng)返回的jfieldID或jmethodID。 表C
※域和方法的函數(shù) 如果你有了一個(gè)類的實(shí)例,它就可以通過(guò)方法GetObjectClass得到,或者如果你沒有這個(gè)類的實(shí)例,可以通過(guò)FindClass得到。符號(hào)是從域的類型或者方法的參數(shù),返回值得到字符串,如表D所示。 表D
※確定域和方法的符號(hào) 下面我們來(lái)看看,如果通過(guò)使用數(shù)組和對(duì)象,從C++中的獲取到Java中的DiskInfo 類對(duì)象,并返回一個(gè)DiskInfo數(shù)組: //返回一個(gè)結(jié)構(gòu)數(shù)組,返回一個(gè)硬盤信息的結(jié)構(gòu)數(shù)組 JNIEXPORT jobjectArray JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_getStructArray (JNIEnv *env, jobject _obj) { //申明一個(gè)object數(shù)組 jobjectArray args = 0; //數(shù)組大小 jsize len = 5; //獲取object所屬類,一般為ava/lang/Object就可以了 jclass objClass = (env)->FindClass("java/lang/Object"); //新建object數(shù)組 args = (env)->NewObjectArray(len, objClass, 0); /**//* 下面為獲取到Java中對(duì)應(yīng)的實(shí)例類中的變量*/ //獲取Java中的實(shí)例類 jclass objectClass = (env)->FindClass("com/sundy/jnidemo/DiskInfo"); //獲取類中每一個(gè)變量的定義 //名字 jfieldID str = (env)->GetFieldID(objectClass,"name","Ljava/lang/String;"); //序列號(hào) jfieldID ival = (env)->GetFieldID(objectClass,"serial","I"); //給每一個(gè)實(shí)例的變量付值,并且將實(shí)例作為一個(gè)object,添加到objcet數(shù)組中 for(int i=0; i < len; i++ ) { //給每一個(gè)實(shí)例的變量付值 jstring jstr = WindowsTojstring(env,"我的磁盤名字是 D:"); //(env)->SetObjectField(_obj,str,(env)->NewStringUTF("my name is D:")); (env)->SetObjectField(_obj,str,jstr); (env)->SetShortField(_obj,ival,10); //添加到objcet數(shù)組中 (env)->SetObjectArrayElement(args, i, _obj); } //返回object數(shù)組 return args; } 全部的C/C++方法實(shí)現(xiàn)代碼如下: /**//* * * 一縷陽(yáng)光(sundy)版權(quán)所有,保留所有權(quán)利。 */ /**//** * * TODO Jni 中一個(gè)從Java到C/C++參數(shù)傳遞測(cè)試類 * * @author 劉正偉(sundy) * @see http://www./sundy * @see mailto:sundy26@126.com * @version 1.0 * @since 2005-4-30 * * 修改記錄: * * 日期 修改人 描述 * ---------------------------------------------------------------------------------------------- * * * */ // JniManage.cpp : 定義 DLL 應(yīng)用程序的入口點(diǎn)。 // package com.sundy.jnidemo; #include "stdafx.h" #include <stdio.h> #include <math.h> #include "jni.h" #include "jni_md.h" #include "./head/Base.h" #include "head/wmi.h" #include "head/com_sundy_jnidemo_ChangeMethodFromJni.h" //通過(guò)javah –jni javactransfer 生成 #include <stdio.h> #include "stdlib.h" #include "string.h" #pragma comment (lib,"BaseInfo.lib") #pragma comment (lib,"jvm.lib") //硬盤信息 struct { char name[256]; int serial; }DiskInfo; /**//*BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { LPTSTR strName = new CHAR[256] ; (*GetHostName)(strName); printf("%s\n",strName); delete [] strName; return TRUE; }*/ //將jstring類型轉(zhuǎn)換成windows類型 char* jstringToWindows( JNIEnv *env, jstring jstr ); //將windows類型轉(zhuǎn)換成jstring類型 jstring WindowsTojstring( JNIEnv* env, char* str ); //主函數(shù) BOOL WINAPI DllMain(HANDLE hHandle, DWORD dwReason, LPVOID lpReserved) { return TRUE; } //輸入常用的數(shù)值類型 Boolean,Byte,Char,Short,Int,Float,Double JNIEXPORT void JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_displayParms (JNIEnv *env, jobject obj, jstring s, jint i, jboolean b) { const char* szStr = (env)->GetStringUTFChars(s, 0 ); printf( "String = [%s]\n", szStr ); printf( "int = %d\n", i ); printf( "boolean = %s\n", (b==JNI_TRUE ? "true" : "false") ); (env)->ReleaseStringUTFChars(s, szStr ); } //調(diào)用一個(gè)靜態(tài)方法,只有一個(gè)簡(jiǎn)單類型輸出 JNIEXPORT jint JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_add (JNIEnv *env, jobject, jint a, jint b) { int rtn = (int)(a + b); return (jint)rtn; } /**/////輸入一個(gè)數(shù)組,這里輸入的是一個(gè)Boolean類型的數(shù)組 JNIEXPORT void JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_setArray (JNIEnv *env, jobject, jbooleanArray ba) { jboolean* pba = (env)->GetBooleanArrayElements(ba, 0 ); jsize len = (env)->GetArrayLength(ba); int i=0; // change even array elements for( i=0; i < len; i+=2 ) { pba[i] = JNI_FALSE; printf( "boolean = %s\n", (pba[i]==JNI_TRUE ? "true" : "false") ); } (env)->ReleaseBooleanArrayElements(ba, pba, 0 ); } /**/////返回一個(gè)字符串?dāng)?shù)組 JNIEXPORT jobjectArray JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_getStringArray (JNIEnv *env, jobject) { jstring str; jobjectArray args = 0; jsize len = 5; char* sa[] = { "Hello,", "world!", "JNI", "is", "fun" }; int i=0; args = (env)->NewObjectArray(len,(env)->FindClass("java/lang/String"),0); for( i=0; i < len; i++ ) { str = (env)->NewStringUTF(sa[i] ); (env)->SetObjectArrayElement(args, i, str); } return args; } //返回一個(gè)結(jié)構(gòu),這里返回一個(gè)硬盤信息的簡(jiǎn)單結(jié)構(gòu)類型 JNIEXPORT jobject JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_getStruct (JNIEnv *env, jobject obj) { /**//* 下面為獲取到Java中對(duì)應(yīng)的實(shí)例類中的變量*/ //獲取Java中的實(shí)例類 jclass objectClass = (env)->FindClass("com/sundy/jnidemo/DiskInfo"); //獲取類中每一個(gè)變量的定義 //名字 jfieldID str = (env)->GetFieldID(objectClass,"name","Ljava/lang/String;"); //序列號(hào) jfieldID ival = (env)->GetFieldID(objectClass,"serial","I"); //給每一個(gè)實(shí)例的變量付值 (env)->SetObjectField(obj,str,(env)->NewStringUTF("my name is D:")); (env)->SetShortField(obj,ival,10); return obj; } //返回一個(gè)結(jié)構(gòu)數(shù)組,返回一個(gè)硬盤信息的結(jié)構(gòu)數(shù)組 JNIEXPORT jobjectArray JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_getStructArray (JNIEnv *env, jobject _obj) { //申明一個(gè)object數(shù)組 jobjectArray args = 0; //數(shù)組大小 jsize len = 5; //獲取object所屬類,一般為ava/lang/Object就可以了 jclass objClass = (env)->FindClass("java/lang/Object"); //新建object數(shù)組 args = (env)->NewObjectArray(len, objClass, 0); /**//* 下面為獲取到Java中對(duì)應(yīng)的實(shí)例類中的變量*/ //獲取Java中的實(shí)例類 jclass objectClass = (env)->FindClass("com/sundy/jnidemo/DiskInfo"); //獲取類中每一個(gè)變量的定義 //名字 jfieldID str = (env)->GetFieldID(objectClass,"name","Ljava/lang/String;"); //序列號(hào) jfieldID ival = (env)->GetFieldID(objectClass,"serial","I"); //給每一個(gè)實(shí)例的變量付值,并且將實(shí)例作為一個(gè)object,添加到objcet數(shù)組中 for(int i=0; i < len; i++ ) { //給每一個(gè)實(shí)例的變量付值 jstring jstr = WindowsTojstring(env,"我的磁盤名字是 D:"); //(env)->SetObjectField(_obj,str,(env)->NewStringUTF("my name is D:")); (env)->SetObjectField(_obj,str,jstr); (env)->SetShortField(_obj,ival,10); //添加到objcet數(shù)組中 (env)->SetObjectArrayElement(args, i, _obj); } //返回object數(shù)組 return args; } //將jstring類型轉(zhuǎn)換成windows類型 char* jstringToWindows( JNIEnv *env, jstring jstr ) { int length = (env)->GetStringLength(jstr ); const jchar* jcstr = (env)->GetStringChars(jstr, 0 ); char* rtn = (char*)malloc( length*2+1 ); int size = 0; size = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)jcstr, length, rtn,(length*2+1), NULL, NULL ); if( size <= 0 ) return NULL; (env)->ReleaseStringChars(jstr, jcstr ); rtn[size] = 0; return rtn; } //將windows類型轉(zhuǎn)換成jstring類型 jstring WindowsTojstring( JNIEnv* env, char* str ) { jstring rtn = 0; int slen = strlen(str); unsigned short * buffer = 0; if( slen == 0 ) rtn = (env)->NewStringUTF(str ); else { int length = MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen, NULL, 0 ); buffer = (unsigned short *)malloc( length*2 + 1 ); if( MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen, (LPWSTR)buffer, length ) >0 ) rtn = (env)->NewString( (jchar*)buffer, length ); } if( buffer ) free( buffer ); return rtn; } Java 測(cè)試native代碼 這沒有什么多說(shuō)的,看代碼吧 //主測(cè)試程序 public static void main(String[] args) { ChangeMethodFromJni changeJni = new ChangeMethodFromJni(); //輸入常用的數(shù)值類型(string int boolean) System.out .println("------------------輸入常用的數(shù)值類型(string int boolean)-----------"); changeJni.displayParms("Hello World!", 100, true); //調(diào)用一個(gè)靜態(tài)方法 System.out.println("------------------調(diào)用一個(gè)靜態(tài)方法-----------"); int ret = changeJni.add(12, 20); System.out.println("The result is: " + String.valueOf(ret)); //輸入一個(gè)數(shù)組 System.out.println("------------------輸入一個(gè)數(shù)組-----------"); boolean[] blList = new boolean[] { true, false, true }; changeJni.setArray(blList); //返回一個(gè)字符串?dāng)?shù)組 System.out.println("------------------返回一個(gè)字符串?dāng)?shù)組-----------"); String[] strList = changeJni.getStringArray(); for (int i = 0; i < strList.length; i++) { System.out.print(strList[i]); } System.out.println(); System.out.println("------------------返回一個(gè)結(jié)構(gòu)-----------"); //返回一個(gè)結(jié)構(gòu) DiskInfo disk = changeJni.getStruct(); System.out.println("name:" + disk.name); System.out.println("Serial:" + disk.serial); //返回一個(gè)結(jié)構(gòu)數(shù)組 System.out.println("------------------返回一個(gè)結(jié)構(gòu)數(shù)組 -----------"); DiskInfo[] diskList = changeJni.getStructArray(); for (int i = 0; i < diskList.length; i++) { System.out.println("name:" + diskList[i].name); System.out.println("Serial:" + diskList[i].serial); } } |
|
來(lái)自: champion_xu > 《Java》