JNI中枚舉類型做爲參數

參考資料:使用JNI進行Java與C/C++語言混合編程(1)--在Java中調用C/C++本地庫html

     java native方法及JNI實例java

JNI是Java Native Interface的英文縮寫,中文翻譯爲本地調用, 自從Java 1.1開始就成爲了Java標準的一部分.linux

C/C++是系統級的編程語言, 能夠用來開發任何和系統相關的程序和類庫, 可是Java自己編寫底層的應用比較難實現, 使用JNI能夠調用現有的本地庫, 極大地靈活了Java的開發.編程

C/C++的效率是目前最好的語言, 可使用C/C++來實現一些實時性很是高的部分. C/C++和Java自己都是很是流行的編程語言, 一些大型軟件中常用語言之間的混合編程.windows

鑑於目前網絡上JNI的文章不是特別多, 我將本身的一些總結寫在這裏. 若有錯漏, 歡迎指正!api

Java調用C/C++大概有這樣幾個步驟網絡

  1. 編寫帶有native方法的Java類, 使用javac工具編譯Java類
  2. 使用javah來生成與native方法對應的頭文件
  3. 實現相應的頭文件, 並編譯爲動態連接庫(windows下是.dll, linux下是.so)

1.簡單的JNI-HelloWorld

首先咱們看一下:Linux下JNI技術使用的一個簡單實例oracle

首先,實現的是Java本地接口Hello.java,代碼以下所示:

編程語言

class HelloWorld {

    public native void sayHello();

    static {
        System.loadLibrary("HelloWorld");
    }

    public static void main(String[] args) {
        (new HelloWorld()).sayHello();
    }
}

 

其中,方法聲明爲native,其實HelloWorld類就至關於一個接口,是爲其餘編程語言聲明的接口。System.loadLibrary("HelloWorld");語句是一個static塊,也就是在該HelloWorld類加載的時候進行執行。其中,該語句實現了加載本地的動態鏈接庫(DLL),在Linux平臺下,動態鏈接庫文件是以.so做爲擴展名的,也就是標準對象(Standard Object)。

對該本地接口類進行編譯:

函數

[root@localhost jni]# javac HelloWorld.java

 

接着,經過編譯的HelloWorld.class文件,生成C語言的頭文件,執行命令:

[root@localhost jni]# javah -jni HelloWorld

 

能夠看到,在當前目錄下生成一個HelloWorld.h文件,該文件就是C的接口文件,爲使用C實現Java接口中定義的方法,能夠發如今HelloWorld.h中有一個方法聲明:

/* DO NOT EDIT THIS FILE - it is machine generated */

#ifndef __HelloWorld__
#define __HelloWorld__

#include <jni.h>

#ifdef __cplusplus
extern "C"
{
#endif

JNIEXPORT void JNICALL Java_HelloWorld_sayHello (JNIEnv *env, jobject);

#ifdef __cplusplus
}
#endif

#endif /* __HelloWorld__ */

 


而後,用C實現該方法,在HelloWorld.c文件中,代碼以下:

#include <jni.h>
#include "HelloWorld.h"
#include <stdio.h>

JNIEXPORT void JNICALL Java_HelloWorld_sayHello (JNIEnv *env, jobject obj) {
    printf("Hello,the World!!!");
}

 

這裏,方法簽名爲Java_HelloWorld_sayHello (JNIEnv *env, jobject obj),添加了形參obj,不然沒法經過編譯。

接下來,生成動態鏈接庫libHelloWorld.so,執行命令:

[root@localhost jni]# gcc -fPIC -shared -o libHelloWorld.so HelloWorld.c

 

(這裏可能會遇到jni.h找不到的狀況,再你配置的JAVA_HOME 文件夾目錄下加上以下路徑選項便可:

-I $JAVA_HOME)/include -I $JAVA_HOME/include/linux


能夠在當前目錄下看到libHelloWorld.so,動態鏈接庫文件名稱以lib開頭。將該文件拷貝到usr/lib目錄下面,就能夠測試了。

如今執行以下命令進行測試:

[root@localhost jni]# java HelloWorld

 

輸出以下:

Hello,the World!!!

這只是一個很是簡單的例子,主要是瞭解JNI在Linux下該如何用。在實際應用中,可能會很是複雜,而且要記住,一旦使用了JNI技術,系統的可移植性被破壞了。有些應用中,正是基於這種特性實現,好比限制軟件的傳播使用,保護開發商權益,等等。

2.枚舉型做爲本地函數的參數

主要參考了這篇文章:http://blog.csdn.net/hemowolf/article/details/6925243

JAVA 的標準JNI中並無enum類型,可是有jobject對象,那麼類和枚舉類型均可以經過jobject進行傳遞。

  

2.1 建立枚舉類型

每一個枚舉對象都有一個對應的索引值,枚舉類中有一個返回值函數。

package test;
 
public class enumclass {
 
    public enum envelopeType {
        NOT_SPECIFIED(-1), NONE(0), IMAGE(1), BITMAP(2);
 
        private int value;
 
        private envelopeType(int value) {
            this.value = value;
        }
 
        public int getValue() {
            return value;
        }
    }
 
}

2.2 JAVA  本地函數

package test;
 
import test.enumclass.envelopeType;
 
public class enumtest {
 
    private envelopeType ent;
 
    public native static void printValue(envelopeType entype);
 
    public static void main(String[] args) {
 
        System.load("/home/sk/workspace/testenumjni2/jni/test.so");
 
        enumtest t1 = new enumtest();
        envelopeType e1 = null;
        int a = e1.IMAGE.getValue();
        System.out.println("------ the envelopeType value is :" + a);
        envelopeType e2 = envelopeType.NOT_SPECIFIED;
        // e2.IMAGE;
        printValue(e2);
 
    }
 
}

在命令行中(linux環境下)切換到工程的bin目錄下執行以下命令:編譯生成.h文件。

javah  -jni test.enumtest

2.3 JNI 接口部分

根據上步的.h文件,編寫對應的c/cpp文件。

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class test_enumtest */
 
#ifndef _Included_test_enumtest
#define _Included_test_enumtest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     test_enumtest
 * Method:    printValue
 * Signature: (Ltest/enumclass/envelopeType;)V
 */
JNIEXPORT void JNICALL Java_test_enumtest_printValue
(JNIEnv *env, jobject obj1, jobject obj2) {
 
    jclass enumclass= env->GetObjectClass(obj2);
    if(enumclass == NULL) {
        printf(" get enumclass failed\r\n");
        return;
    }
 
    jmethodID getVal = env->GetMethodID(enumclass, "getValue", "()I");
    jint value = env->CallIntMethod(obj2, getVal);
        printf("------ the envelopeType value is :%d\r\n", value);
 
}
 
#ifdef __cplusplus
}
#endif
#endif

env->GetMethodID 有三個參數,第一個表示獲取的類的名字,第二個參數是函數名,最後一個參數應該這樣得到:

這個參數應該是JAVA的類的方法的簽名,它能夠經過jdk的工具 javap 獲得。如上面的 test.enumtest.envelopeType 類中的 getValue 方法,能夠在命令行裏執行:

javap -s test.enumtest.envelopeType

 

獲得 test.enumtest.envelopeType  的全部方法的簽名。

 

2.4 下面是另外一個沒有索引值的枚舉類型列子

考慮本地函數參數中含有枚舉類型的對象,在JNI部分要獲取的是枚舉對象,而非對象的一個函數。

package test;
 
public class enumclass {
 
    public enum DATE {
 
        MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATUDAY, SUNDAY;
    }
 
     
}
import test.enumclass.DATE;
 
public class enumtest {
 
    private DATE date;
    public native static void callobj(DATE date);
     
    public static void main(String[] args){
         
        System.load("/home/sk/workspace/testenumjni3/jni/test.so");
        enumtest e1 = new enumtest();
        e1.date = DATE.SATUDAY;
        System.out.println(e1.date);
        //Now we call the native method
         
        DATE today = DATE.TUESDAY;
        callobj(today);
    }
     
}
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include<string.h>
/* Header for class test_enumtest */
 
#ifndef _Included_test_enumtest
#define _Included_test_enumtest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     test_enumtest
 * Method:    callobj
 * Signature: (Ltest/enumclass/DATE;)V
 */
JNIEXPORT void JNICALL Java_test_enumtest_callobj
  (JNIEnv *env, jobject obj1, jobject obj2){
 
    jclass enumclass= env->GetObjectClass(obj2);
        if(enumclass == NULL) {
            printf(" get enumclass failed\r\n");
            return;
        }
 
        jmethodID getVal = env->GetMethodID(enumclass, "name", "()Ljava/lang/String;");
        jstring value = (jstring)env->CallObjectMethod(obj2, getVal);
        const char * valueNative = env->GetStringUTFChars(value, 0);
 
        if (strcmp(valueNative, "MONDAY") == 0) {
        printf("TODAY IS MONDAY!");
        }
        else
            printf("TODAY IS  NOT MONDAY!");
 
 
 
}
 
#ifdef __cplusplus
}
#endif
#endif

jmethodID getVal = env->GetMethodID(enumclass, "name", "()Ljava/lang/String;");

這裏name()函數是enum類型自帶的函數,()Ljava/lang/String;是對應的簽名。具體的ENUM類型能夠參考官方文檔:

http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Enum.html

另外就是這裏換成了(jstring)env->CallObjectMethod(obj2, getVal);方法。

 

3.涉及C/C++與JAVA類型轉換的問題

 下面的一個例子,是要在JNI中完成由C++寫的代碼函數的調用,函數的參數包括枚舉類型的。須要在JNI中對原來C/C++定義的枚舉型從新作一次映射(在數據量小的時候這樣操做是可行的)。

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include<string.h>
/*emun類型定義*/
enum DATE {
   
        MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATUDAY, SUNDAY;
    }
 void returndate(DATE date);
 void returndate(DATE date){
    printf("%d/n","TODAY IS :");
    switch(date){
    case MONDAY:
     printf("%d/n","MONDAY");
    break;
    case TUESDAY:
         printf("%d/n","TUESDAY");
         break;
    case WEDNESDAY:
         printf("%d/n","WEDNESDAY");
         break;
    case THURSDAY:
         printf("%d/n","THURSDAY");
         break;
    case FRIDAY:
         printf("%d/n","FRIDAY");
         break;
    case SATUDAY:
         printf("%d/n","SATUDAY");
         break;
     case SUNDAY:
         printf("%d/n","SUNDAY");
}
}
/* Header for class test_enumtest */
   
#ifndef _Included_test_enumtest
#define _Included_test_enumtest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     test_enumtest
 * Method:    callobj
 * Signature: (Ltest/enumclass/DATE;)V
 */
JNIEXPORT void JNICALL Java_test_enumtest_callobj
  (JNIEnv *env, jobject obj1, jobject obj2){
jclass enumclass= env->GetObjectClass(obj2);
        if(enumclass == NULL) {
            printf(" get enumclass failed\r\n");
            return;
        }
   
        jmethodID getVal = env->GetMethodID(enumclass, "name", "()Ljava/lang/String;");
        jstring value = (jstring)env->CallObjectMethod(obj2, getVal);
        const char * valueNative = env->GetStringUTFChars(value, 0);
         DATE date;
        //這裏完成一個轉換
        if (strcmp(valueNative, "MONDAY") == 0) {
         date = MONDAY;
        }
        if.....(這裏接着判斷)
 returndate(date);
   
   
}
   
#ifdef __cplusplus
}
#endif
#endif

若是在JNI函數中調用C++的函數中帶有枚舉型參數,用jstring類型或者 char*類型的作參數都會報錯,能夠作的就是再作一次名字的映射,將上述類型映射成enmu類型,再看成參數去傳遞,就不會有問題了。

相關文章
相關標籤/搜索