C++和JNI的數據轉換

如何使用JNI的一些基本方法和過程在網上多如牛毛,若是你對Jni不甚瞭解,不知道Jni是作什麼的,如何創建一個基本的jni程序,或許能夠參考下面下面這些文章:
利用VC++6.0實現JNI的最簡單的例子 
JNI入門教程之HelloWorld篇 
SUN JNI Tutorial

這些資料的例子中,大多數只是輸入一些簡單的參數,獲取沒有參數。而在實際的使用過程當中,每每須要對參數進行處理轉換。才能夠被C/C++程序識別。好比咱們在C++中有一個結構(Struct)DiskInfo ,須要傳遞一個相似於DiskInfo *pDiskInfo的參數,相似於在C++這樣參數如何傳遞到Java中呢?下面咱們就來討論C++到Java中方法的一些常見參數的轉換:
html

定義Native Java類:

若是你習慣了使用JNI,你就不會以爲它難了。既然本地方法是由其餘語言實現的,它們在Java中沒有函數體。可是,全部本地代碼必須用本地關鍵詞聲明,成爲Java類的成員。假設咱們在C++中有這麼一個結構,它用來描述硬盤信息: java

// 硬盤信息
struct   {
    char name[256];
    int serial;
}
DiskInfo;

那麼咱們須要在Java中定義一個類來與之匹配,聲明能夠寫成這樣: web

class DiskInfo  {
    //名字
    public String name;

    //序列號
    public int serial;
}

在這個類中,申明一些Native的本地方法,來測試方法參數的傳遞,分別定義了一些函數,用來傳遞結構或者結構數組,具體定義以下面代碼: windows

/****************** 定義本地方法 ********************/
     // 輸入經常使用的數值類型(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++頭文件。
一、在控制檯下進入工做路徑,本工程路徑爲:E:\work\java\workspace\JavaJni。
二、運行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調用操做。 eclipse

表A jvm

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類型映射
ide

使用數組:

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函數。每一個函數接受(做爲參數)域或方法的類,它們的名稱,符號和它們對應返回的jfieldIDjmethodID

表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:sundy26@126.com
*  @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[] truefalsetrue };
        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);
        }


    }
注:本程序在VS2003,eclipse (jse5.0) winxp sp2編譯經過
相關文章
相關標籤/搜索