1. 程式人生 > >Jni中C++和Java的引數傳遞

Jni中C++和Java的引數傳遞

如何使用JNI的一些基本方法和過程在網上多如牛毛,如果你對Jni不甚瞭解,不知道Jni是做什麼的,如何建立一個基本的jni程式,或許可以參考下面下面這些文章:

<利用VC++6.0實現JNI的最簡單的例子>

<JNI入門教程之HelloWorld篇>

<SUN JNI Tutorial>

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

1.定義Native Java類:

如果你習慣了使用JNI,你就不會覺得它難了。既然本地方法是由其他語言實現的,它們在Java中沒有函式體。但是,所有原生代碼必須用本地關鍵詞native宣告,成為Java類的成員。假設我們在C++中有這麼一個結構,它用來描述硬碟資訊:

//硬碟資訊

struct {

    char name[256];

    int serial;

}DiskInfo;

那麼我們需要在Java中定義一個類來與之匹配,宣告可以寫成這樣:

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();

2.編譯生成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

3.在C/C++中實現本地方法

生 成C/C++標頭檔案之後,你就需要寫標頭檔案對應的本地方法。注意:所有的本地方法的第一個引數都是指向JNIEnv結構的。這個結構是用來呼叫JNI函式 的。第二個引數jclass的意義,要看方法是不是靜態的(static)或者例項(Instance)的。前者,jclass代表一個類物件的引用,而 後者是被呼叫的方法所屬物件的引用。

返回值和引數型別根據等價約定對映到本地C/C++型別,如表JNI型別對映所示。有些型別,在原生代碼中可直接使用,而其他型別只有通過JNI呼叫操作。

表A ※   JNI 型別對映

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 雙浮點型陣列

3.1 使用陣列:

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函式會返回陣列的長度。

3.2 使用物件

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所屬類,一般為java/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);

        }

    }

相關推薦

JniC++Java引數傳遞

如何使用JNI的一些基本方法和過程在網上多如牛毛,如果你對Jni不甚瞭解,不知道Jni是做什麼的,如何建立一個基本的jni程式,或許可以參考下面下面這些文章: <利用VC++6.0實現JNI的最簡單的例子> <JNI入門教程之HelloWorld篇&

Android JNICJAVA程式碼之間的互相呼叫

jobject (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...); jobject (*CallObjectMethodV)(JNIEnv*, jobject, jmethodID, va_list); jobject

JNI開發-C/C++呼叫Java函式傳遞多個引數

       C/C++呼叫Java函式傳遞多個引數並返回String型別; Java的CCallJavaTools類: package com.niubashaoye.ndk.jni; public class CCallJavaTools { /** * C

C#JAVA編寫事務代碼

com creat .class eas ++ acc data savepoint return C# DAL層代碼,運行多條增刪改,使用事務操作: /// <summary> /// 運行 多條增刪改 (非查詢語句)

c#Java的多態

rtu 重新 指定 demo har ati static lse 編譯器 多態:讓一個對象表現出多種類型,寫出通用的代碼,最大限度的屏蔽各個子類之間的差異性。 c#舉例: 1 將父類的方法標記為虛方法 ,使用關鍵字 virtual,這個函數可以被子類重新寫一個遍。

JNIc/c++Java互動,呼叫java成員

public class JniTest2 { //c訪問非靜態成員 public String testField="hello..."; //c修改java靜態成員 public static int time=78; //c訪問java 方

vuerouter-link的引數傳遞接收

1 第一種方法: 設定要傳遞的引數 路由檔案的配置: 此時不需要做任何的更改 其他元件來獲取傳遞的引數: 在url上表現的顯示: 點選其中的詳細資訊 第二種方法: 設定要傳遞的引數 此時路由檔案需要簡單的配置一下: 注意:兩處的ic名稱一定要一樣 ,和c#

android jni開發c++ 呼叫java 方法

    最近幾天搞fbreader 電子書的二次開發,其中需要 c++ 呼叫 java方法解密電子書,所以l老虎吃天,硬著頭皮看c++程式碼。  具體的思路如下,其實也就這幾步:      1. jni 中用到 java反射的方法

java 的 方法的引數傳遞

1:形參:方法宣告時,方法小括號內的引數 2:實參:呼叫方法時,實際傳入的引數的值 3:規則:java中的引數傳遞機制:值傳遞機制 1)形參是基本資料型別的;將實參的值傳遞給形參的基本資料型別的變數 public class PassObject{ public

matlabc++混合程式設計---Mex結構mexFunction引數傳遞

最近的專案需要matlab和C的混合程式設計,經過一番努力終於完成了專案要解決的問題。現在就將Mex的一些經驗總結一下,當然只是剛剛開始,以後隨著學習的深入繼續新增。首先講講寫Mex的一些常規規定,然後我們會重點關注混合程式設計中最難解決資料的問題--結構到底如何轉換

CJavastatic修飾符的作用

C中的static C語言中的static作用有兩個:其一,增加區域性變數的生命週期,將其升級為全域性變數;其二,宣告變數或常量不可被其他檔案直接引用,必須通過標頭檔案包含的方式。 --------

c#java的值傳遞引用傳遞問題

往往我們在程式設計時會思考傳進方法體內的物件是引用傳遞還是值傳遞我們先看看java是怎麼玩的public class Class1 { public int i = 0; public void pr() { System.out.pr

js的型別函式引數傳遞型別問題

js中的型別: 2大型別:原始型別和物件。 原始型別有 boolean、number、string這三個普通原始型別,還有null、undefined這倆特殊原始型別 物件嘛就多了,普通物件、內建物件、全域性物件、函式、陣列等。 函式引數傳遞型別:   對於原始型別,傳遞的是值,

關於Java的引用函式引數傳遞

Java中沒有了指標,這有時給程式設計師帶來了些許不便。Java的語言設計者強調,這種不便可以通過Java的引用特性得到彌補。即對於Java的任何物件,我們可以申明物件變數,但不產生例項,這樣,把該變數指向具有實際例項的物件,即可實現同一例項物件的多個變數引用,如: int

JavaSystem.out.printf引數傳遞錯誤分析與修正

在Eclipse中使用System.out.printf方法進行格式化列印時,如:System.out.printf("%5d", i), 會提示錯誤描述:The method printf(Stri

C++java異常處理關於finally的區別

在java中,異常處理由try{}catch(){}finally{}組成,無論try中有沒有異常,try和catch中有沒有return,finally最終都會執行。而且finally中若有return,則此return和try、catch中的return相比

AjaxPutDelete請求傳遞引數無效的解決方法(Restful風格

在使用Ajax實現Restful的時候,有時候會出現無法Put、Delete請求引數無法傳遞到程式中的尷尬情況,此時我們可以有兩種解決方案:1、使用地址重寫的方法傳遞引數。2、配置web.xml專案環境。 測試的程式為: @RequestMapping(value =

JavaString型別的引數傳遞問題的解析

一、引入示例 Java程式碼   <span style="font-size: small;">public class StringAsParamOfMethodDemo {       public static void main(Strin

JNI學習(一)(cjava層物件互相呼叫)

c層呼叫java物件 package com.example.bean; /** * * java物件 * @author telenewbie * */ public class JNI_cCalljava_test { publi

AjaxPutDelete請求傳遞引數無效的解決方法(Restful風格)

開發環境:Tomcat9.0 在使用Ajax實現Restful的時候,有時候會出現無法Put、Delete請求引數無法傳遞到程式中的尷尬情況,此時我們可以有兩種解決方案:1、使用地址重寫的方法傳遞引數。2、配置web.xml專案環境。 測試的程式為: