轉自:http://dolphin-ygj.javaeye.com/blog/519489
如何使用JNI的一些基本方法和過程在網上多如牛毛,如果你對Jni不甚瞭解,不知道Jni是做什麼的,如何建立一個基本的jni程序,或許可以參考下面下面這些文章:
利用VC++6.0實現JNI的最簡單的例子
JNI入門教程之HelloWorld篇
SUN JNI Tutorial
這些資料的例子中,大多數只是輸入一些簡單的參數,獲取沒有參數。而在實際的使用過程中,往往需要對參數進行處理轉換。纔可以被C/C++程序識別。比如我們在C++中有一個結構(Struct)DiskInfo ,需要傳遞一個類似於DiskInfo *pDiskInfo的參數,類似於在C++這樣參數如何傳遞到Java中呢?下面我們就來討論C++到Java中方法的一些常見參數的轉換:
定義Native Java類:
如果你習慣了使用JNI,你就不會覺得它難了。既然本地方法是由其他語言實現的,它們在Java中沒有函數體。但是,所有本地代碼必須用本地關鍵詞聲明,成爲Java類的成員。假設我們在C++中有這麼一個結構,它用來描述硬盤信息:
- //硬盤信息
- struct {
- char name[256];
- int serial;
- }DiskInfo;
- //硬盤信息
- struct {
- char name[256];
- int serial;
- }DiskInfo;
//硬盤信息
struct {
char name[256];
int serial;
}DiskInfo;
那麼我們需要在Java中定義一個類來與之匹配,聲明可以寫成這樣:
- class DiskInfo {
- //名字
- public String name;
- //序列號
- public int serial;
- }
- class DiskInfo {
- //名字
- public String name;
- //序列號
- public int serial;
- }
class DiskInfo {
//名字
public String name;
//序列號
public int serial;
}
在這個類中,申明一些Native的本地方法,來測試方法參數的傳遞,分別定義了一些函數,用來傳遞結構或者結構數組,具體定義如下面代碼:
- /**//****************** 定義本地方法 ********************/
- //輸入常用的數值類型(Boolean,Byte,Char,Short,Int,Float,Double)
- public native void displayParms(String showText, int i, boolean bl);
- //調用一個靜態方法
- public native int add(int a, int b);
- //輸入一個數組
- public native void setArray(boolean[] blList);
- //返回一個字符串數組
- public native String[] getStringArray();
- //返回一個結構
- public native DiskInfo getStruct();
- //返回一個結構數組
- public native DiskInfo[] getStructArray();
- /**//****************** 定義本地方法 ********************/
- //輸入常用的數值類型(Boolean,Byte,Char,Short,Int,Float,Double)
- public native void displayParms(String showText, int i, boolean bl);
- //調用一個靜態方法
- public native int add(int a, int b);
- //輸入一個數組
- public native void setArray(boolean[] blList);
- //返回一個字符串數組
- public native String[] getStringArray();
- //返回一個結構
- public native DiskInfo getStruct();
- //返回一個結構數組
- public native DiskInfo[] getStructArray();
/**//****************** 定義本地方法 ********************/
//輸入常用的數值類型(Boolean,Byte,Char,Short,Int,Float,Double)
public native void displayParms(String showText, int i, boolean bl);
//調用一個靜態方法
public native int add(int a, int b);
//輸入一個數組
public native void setArray(boolean[] blList);
//返回一個字符串數組
public native String[] getStringArray();
//返回一個結構
public native DiskInfo getStruct();
//返回一個結構數組
public native DiskInfo[] getStructArray();
編譯生成C/C++頭文件
定義好了Java類之後,接下來就要寫本地代碼。本地方法符號提供一個滿足約定的頭文件,使用Java工具Javah可以很容易地創建它而不用手動去創建。你對Java的class文件使用javah命令,就會爲你生成一個對應的C/C++頭文件。
1、在控制檯下進入工作路徑,本工程路徑爲:E:/work/java/workspace/JavaJni。
2、運行javah 命令:javah -classpath E:/work/java/workspace/JavaJni com.sundy.jnidemo ChangeMethodFromJni
本文生成的C/C++頭文件名爲: com_sundy_jnidemo_ChangeMethodFromJni.h
在C/C++中實現本地方法
生成C/C++頭文件之後,你就需要寫頭文件對應的本地方法。注意:所有的本地方法的第一個參數都是指向JNIEnv結構的。這個結構是用來調用JNI函數的。第二個參數jclass的意義,要看方法是不是靜態的(static)或者實例(Instance)的。前者,jclass代表一個類對象的引用,而後者是被調用的方法所屬對象的引用。
返回值和參數類型根據等價約定映射到本地C/C++類型,如表JNI類型映射所示。有些類型,在本地代碼中可直接使用,而其他類型只有通過JNI調用操作。
表A
Java類型 本地類型 描述
boolean jboolean C/C++8位整型
byte jbyte C/C++帶符號的8位整型
char jchar C/C++無符號的16位整型
short jshort C/C++帶符號的16位整型
int jint C/C++帶符號的32位整型
long jlong C/C++帶符號的64位整型e
float jfloat C/C++32位浮點型
double jdouble C/C++64位浮點型
Object jobject 任何Java對象,或者沒有對應java類型的對象
Class jclass Class對象
String jstring 字符串對象
Object[] jobjectArray 任何對象的數組
boolean[] jbooleanArray 布爾型數組
byte[] jbyteArray 比特型數組
char[] jcharArray 字符型數組
short[] jshortArray 短整型數組
int[] jintArray 整型數組
long[] jlongArray 長整型數組
float[] jfloatArray 浮點型數組
double[] jdoubleArray 雙浮點型數組
※ JNI類型映射
使用數組:
JNI通過JNIEnv提供的操作Java數組的功能。它提供了兩個函數:一個是操作java的簡單型數組的,另一個是操作對象類型數組的。
因爲速度的原因,簡單類型的數組作爲指向本地類型的指針暴露給本地代碼。因此,它們能作爲常規的數組存取。這個指針是指向實際的Java數組或者Java數組的拷貝的指針。另外,數組的佈置保證匹配本地類型。
爲了存取Java簡單類型的數組,你就要要使用GetXXXArrayElements函數(見表B),XXX代表了數組的類型。這個函數把Java數組看成參數,返回一個指向對應的本地類型的數組的指針。
表B
函數 Java數組類型 本地類型
GetBooleanArrayElements jbooleanArray jboolean
GetByteArrayElements jbyteArray jbyte
GetCharArrayElements jcharArray jchar
GetShortArrayElements jshortArray jshort
GetIntArrayElements jintArray jint
GetLongArrayElements jlongArray jlong
GetFloatArrayElements jfloatArray jfloat
GetDoubleArrayElements jdoubleArray jdouble
JNI數組存取函數
當你對數組的存取完成後,要確保調用相應的ReleaseXXXArrayElements函數,參數是對應Java數組和 GetXXXArrayElements返回的指針。如果必要的話,這個釋放函數會複製你做的任何變化(這樣它們就反射到java數組),然後釋放所有相關的資源。
爲了使用java對象的數組,你必須使用GetObjectArrayElement函數和SetObjectArrayElement函數,分別去get,set數組的元素。GetArrayLength函數會返回數組的長度。
使用對象
JNI提供的另外一個功能是在本地代碼中使用Java對象。通過使用合適的JNI函數,你可以創建 Java對象,get、set 靜態(static)和實例(instance)的域,調用靜態(static)和實例(instance)函數。JNI通過ID識別域和方法,一個域或方法的ID是任何處理域和方法的函數的必須參數。
表C列出了用以得到靜態(static)和實例(instance)的域與方法的JNI函數。每個函數接受(作爲參數)域或方法的類,它們的名稱,符號和它們對應返回的jfieldID或jmethodID。
表C
函數 描述
GetFieldID 得到一個實例的域的ID
GetStaticFieldID 得到一個靜態的域的ID
GetMethodID 得到一個實例的方法的ID
GetStaticMethodID 得到一個靜態方法的ID
※域和方法的函數
如果你有了一個類的實例,它就可以通過方法GetObjectClass得到,或者如果你沒有這個類的實例,可以通過FindClass得到。符號是從域的類型或者方法的參數,返回值得到字符串,如表D所示。
表D
Java 類型 符號
boolean Z
byte B
char C
short S
int I
long L
float F
double D
void V
objects對象 Lfully-qualified-class-name;L類名
Arrays數組 [array-type [數組類型
methods方法 (argument-types)return-type(參數類型)返回類型
※確定域和方法的符號
下面我們來看看,如果通過使用數組和對象,從C++中的獲取到Java中的DiskInfo 類對象,並返回一個DiskInfo數組:
- //返回一個結構數組,返回一個硬盤信息的結構數組
- JNIEXPORT jobjectArray JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_getStructArray
- (JNIEnv *env, jobject _obj)
- {
- //申明一個object數組
- jobjectArray args = 0;
- //數組大小
- jsize len = 5;
- //獲取object所屬類,一般爲ava/lang/Object就可以了
- jclass objClass = (env)->FindClass("java/lang/Object");
- //新建object數組
- args = (env)->NewObjectArray(len, objClass, 0);
- /**//* 下面爲獲取到Java中對應的實例類中的變量*/
- //獲取Java中的實例類
- jclass objectClass = (env)->FindClass("com/sundy/jnidemo/DiskInfo");
- //獲取類中每一個變量的定義
- //名字
- jfieldID str = (env)->GetFieldID(objectClass,"name","Ljava/lang/String;");
- //序列號
- jfieldID ival = (env)->GetFieldID(objectClass,"serial","I");
- //給每一個實例的變量付值,並且將實例作爲一個object,添加到objcet數組中
- for(int i=0; i < len; i++ )
- {
- //給每一個實例的變量付值
- 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數組中
- (env)->SetObjectArrayElement(args, i, _obj);
- }
- //返回object數組
- return args;
- }
- 全部的C/C++方法實現代碼如下:
- /**//*
- *
- * 一縷陽光(sundy)版權所有,保留所有權利。
- */
- /**//**
- *
- * TODO Jni 中一個從Java到C/C++參數傳遞測試類
- *
- * @author 劉正偉(sundy)
- * @see http://www.cnweblog.com/sundy
- * @see mailto:[email protected]
- * @version 1.0
- * @since 2005-4-30
- *
- * 修改記錄:
- *
- * 日期 修改人 描述
- * ----------------------------------------------------------------------------------------------
- *
- *
- *
- */
- // JniManage.cpp : 定義 DLL 應用程序的入口點。
- //
- 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" //通過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類型轉換成windows類型
- char* jstringToWindows( JNIEnv *env, jstring jstr );
- //將windows類型轉換成jstring類型
- jstring WindowsTojstring( JNIEnv* env, char* str );
- //主函數
- BOOL WINAPI DllMain(HANDLE hHandle, DWORD dwReason, LPVOID lpReserved)
- {
- return TRUE;
- }
- //輸入常用的數值類型 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 );
- }
- //調用一個靜態方法,只有一個簡單類型輸出
- JNIEXPORT jint JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_add
- (JNIEnv *env, jobject, jint a, jint b)
- {
- int rtn = (int)(a + b);
- return (jint)rtn;
- }
- /**/////輸入一個數組,這裏輸入的是一個Boolean類型的數組
- 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 );
- }
- /**/////返回一個字符串數組
- 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;
- }
- //返回一個結構,這裏返回一個硬盤信息的簡單結構類型
- JNIEXPORT jobject JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_getStruct
- (JNIEnv *env, jobject obj)
- {
- /**//* 下面爲獲取到Java中對應的實例類中的變量*/
- //獲取Java中的實例類
- jclass objectClass = (env)->FindClass("com/sundy/jnidemo/DiskInfo");
- //獲取類中每一個變量的定義
- //名字
- jfieldID str = (env)->GetFieldID(objectClass,"name","Ljava/lang/String;");
- //序列號
- jfieldID ival = (env)->GetFieldID(objectClass,"serial","I");
- //給每一個實例的變量付值
- (env)->SetObjectField(obj,str,(env)->NewStringUTF("my name is D:"));
- (env)->SetShortField(obj,ival,10);
- return obj;
- }
- //返回一個結構數組,返回一個硬盤信息的結構數組
- JNIEXPORT jobjectArray JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_getStructArray
- (JNIEnv *env, jobject _obj)
- {
- //申明一個object數組
- jobjectArray args = 0;
- //數組大小
- jsize len = 5;
- //獲取object所屬類,一般爲ava/lang/Object就可以了
- jclass objClass = (env)->FindClass("java/lang/Object");
- //新建object數組
- args = (env)->NewObjectArray(len, objClass, 0);
- /**//* 下面爲獲取到Java中對應的實例類中的變量*/
- //獲取Java中的實例類
- jclass objectClass = (env)->FindClass("com/sundy/jnidemo/DiskInfo");
- //獲取類中每一個變量的定義
- //名字
- jfieldID str = (env)->GetFieldID(objectClass,"name","Ljava/lang/String;");
- //序列號
- jfieldID ival = (env)->GetFieldID(objectClass,"serial","I");
- //給每一個實例的變量付值,並且將實例作爲一個object,添加到objcet數組中
- for(int i=0; i < len; i++ )
- {
- //給每一個實例的變量付值
- 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數組中
- (env)->SetObjectArrayElement(args, i, _obj);
- }
- //返回object數組
- return args;
- }
- //將jstring類型轉換成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類型轉換成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 測試native代碼
- 這沒有什麼多說的,看代碼吧
- //主測試程序
- public static void main(String[] args) {
- ChangeMethodFromJni changeJni = new ChangeMethodFromJni();
- //輸入常用的數值類型(string int boolean)
- System.out
- .println("------------------輸入常用的數值類型(string int boolean)-----------");
- changeJni.displayParms("Hello World!", 100, true);
- //調用一個靜態方法
- System.out.println("------------------調用一個靜態方法-----------");
- int ret = changeJni.add(12, 20);
- System.out.println("The result is: " + String.valueOf(ret));
- //輸入一個數組
- System.out.println("------------------輸入一個數組-----------");
- boolean[] blList = new boolean[] { true, false, true };
- changeJni.setArray(blList);
- //返回一個字符串數組
- System.out.println("------------------返回一個字符串數組-----------");
- String[] strList = changeJni.getStringArray();
- for (int i = 0; i < strList.length; i++) {
- System.out.print(strList[i]);
- }
- System.out.println();
- System.out.println("------------------返回一個結構-----------");
- //返回一個結構
- DiskInfo disk = changeJni.getStruct();
- System.out.println("name:" + disk.name);
- System.out.println("Serial:" + disk.serial);
- //返回一個結構數組
- System.out.println("------------------返回一個結構數組 -----------");
- 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);
- }
- }