久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

使用JNI進行混合編程:在C/C 中調(diào)用Java代碼

 guozhenno1 2017-06-25

使用JNI進行混合編程:在C/C++中調(diào)用Java代碼
會被淹死的魚  會被淹死的魚博客  2012-04-28

JNI就是Java Native Interface, 即可以實現(xiàn)Java調(diào)用本地庫, 也可以實現(xiàn)C/C++調(diào)用Java代碼, 從而實現(xiàn)了兩種語言的互通, 可以讓我們更加靈活的使用。

通過使用JNI可以從一個側(cè)面了解Java內(nèi)部的一些實現(xiàn),。

本文使用的環(huán)境是:

  1. 64位的win7系統(tǒng)
  2. JDK 1.6.0u30 (32位)
  3. C/C++編譯器是 Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 (VC 6.0的, 其他版本的也可以編譯通過, 測試過vs2010)

本文使用到的一些功能:

  1. 創(chuàng)建虛擬機
  2. 尋找class對象, 創(chuàng)建對象
  3. 調(diào)用靜態(tài)方法和成員方法
  4. 獲取成員屬性, 修改成員屬性

C/C++調(diào)用Java代碼的一般步驟:

  1. 編寫Java代碼, 并編譯
  2. 編寫C/C++代碼
  3. 配置lib進行編譯, 配置PATH添加相應的dll或so并運行

編寫Java代碼并編譯

這段代碼非常簡單, 有個靜態(tài)方法和成員方法, 一個public的成員變量

  1. public class Sample2 {  
  2.     public String name;  
  3.        
  4.     public static String sayHello(String name) {  
  5.         return 'Hello, ' + name + '!';  
  6.     }  
  7.        
  8.     public String sayHello() {  
  9.         return 'Hello, ' + name + '!';  
  10.     }  

由于沒有定義構(gòu)造函數(shù), 所以會有一個默認的構(gòu)造函數(shù).

運行下面的命令編譯

>javac Sample2.java

 可以在當前目錄下看到Sample2.class文件, 編譯成功, 第一步完成了, So easy!

可以查看Sample2類中的簽名

>javap -s -private Sample2

結(jié)果如下

  1. Compiled from 'Sample2.java' 
  2. public class Sample2 extends java.lang.Object{  
  3. public java.lang.String name;  
  4.   Signature: Ljava/lang/String;  
  5. public Sample2();  
  6.   Signature: ()V  
  7. public static java.lang.String sayHello(java.lang.String);  
  8.   Signature: (Ljava/lang/String;)Ljava/lang/String;  
  9. public java.lang.String sayHello();  
  10.   Signature: ()Ljava/lang/String;  

關于簽名的含義, 請參看使用JNI進行Java與C/C++語言混合編程(1)--在Java中調(diào)用C/C++本地庫.

編寫C/C++代碼調(diào)用Java代碼

先貼代碼吧

  1. #include   
  2. #include   
  3. #include   
  4.    
  5. // 環(huán)境變量PATH在windows下和linux下的分割符定義  
  6. #ifdef _WIN32  
  7. #define PATH_SEPARATOR ';' 
  8. #else 
  9. #define PATH_SEPARATOR ':' 
  10. #endif  
  11.    
  12.    
  13. int main(void)  
  14. {  
  15.     JavaVMOption options[1];  
  16.     JNIEnv *env;  
  17.     JavaVM *jvm;  
  18.     JavaVMInitArgs vm_args;  
  19.        
  20.     long status;  
  21.     jclass cls;  
  22.     jmethodID mid;  
  23.     jfieldID fid;  
  24.     jobject obj;  
  25.        
  26.     options[0].optionString = '-Djava.class.path=.';  
  27.     memset(&vm_args, 0, sizeof(vm_args));  
  28.     vm_args.version = JNI_VERSION_1_4;  
  29.     vm_args.nOptions = 1;  
  30.     vm_args.options = options;  
  31.        
  32.     // 啟動虛擬機  
  33.     status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);  
  34.        
  35.     if (status != JNI_ERR)  
  36.     {  
  37.         // 先獲得class對象  
  38.         cls = (*env)->FindClass(env, 'Sample2');  
  39.         if (cls != 0)  
  40.         {  
  41.             // 獲取方法ID, 通過方法名和簽名, 調(diào)用靜態(tài)方法  
  42.             mid = (*env)->GetStaticMethodID(env, cls, 'sayHello''(Ljava/lang/String;)Ljava/lang/String;');  
  43.             if (mid != 0)  
  44.             {  
  45.                 const char* name = 'World';  
  46.                 jstring arg = (*env)->NewStringUTF(env, name);  
  47.                 jstring result = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid, arg);  
  48.                 const char* str = (*env)->GetStringUTFChars(env, result, 0);  
  49.                 printf('Result of sayHello: %s\n', str);  
  50.                 (*env)->ReleaseStringUTFChars(env, result, 0);  
  51.             }  
  52.                
  53.             /*** 新建一個對象 ***/ 
  54.             // 調(diào)用默認構(gòu)造函數(shù)  
  55.             //obj = (*env)->AllocObjdect(env, cls);   
  56.                
  57.             // 調(diào)用指定的構(gòu)造函數(shù), 構(gòu)造函數(shù)的名字叫做  
  58.             mid = (*env)->GetMethodID(env, cls, '''()V');  
  59.             obj = (*env)->NewObject(env, cls, mid);  
  60.             if (obj == 0)  
  61.             {  
  62.                 printf('Create object failed!\n');  
  63.             }  
  64.             /*** 新建一個對象 ***/ 
  65.                
  66.             // 獲取屬性ID, 通過屬性名和簽名  
  67.             fid = (*env)->GetFieldID(env, cls, 'name''Ljava/lang/String;');  
  68.             if (fid != 0)  
  69.             {  
  70.                 const char* name = 'icejoywoo';  
  71.                 jstring arg = (*env)->NewStringUTF(env, name);  
  72.                 (*env)->SetObjectField(env, obj, fid, arg); // 修改屬性  
  73.             }  
  74.                
  75.             // 調(diào)用成員方法  
  76.             mid = (*env)->GetMethodID(env, cls, 'sayHello''()Ljava/lang/String;');  
  77.             if (mid != 0)  
  78.             {  
  79.                 jstring result = (jstring)(*env)->CallObjectMethod(env, obj, mid);  
  80.                 const char* str = (*env)->GetStringUTFChars(env, result, 0);  
  81.                 printf('Result of sayHello: %s\n', str);  
  82.                 (*env)->ReleaseStringUTFChars(env, result, 0);  
  83.             }  
  84.         }  
  85.            
  86.         (*jvm)->DestroyJavaVM(jvm);  
  87.         return 0;  
  88.     }  
  89.     else 
  90.     {  
  91.         printf('JVM Created failed!\n');  
  92.         return -1;  
  93.     }  

這段代碼大概做了這幾件事:

  1. 創(chuàng)建虛擬機JVM, 在程序結(jié)束的時候銷毀虛擬機JVM
  2. 尋找class對象
  3. 創(chuàng)建class對象的實例
  4. 調(diào)用方法和修改屬性

虛擬的創(chuàng)建

與之相關的有這樣幾個變量

  1. JavaVMOption options[1];  
  2. JNIEnv *env;  
  3. JavaVM *jvm;  
  4. JavaVMInitArgs vm_args; 

JavaVM就是我們需要創(chuàng)建的虛擬機實例

JavaVMOption相當于在命令行里傳入的參數(shù)

JNIEnv在Java調(diào)用C/C++中每個方法都會有的一個參數(shù), 擁有一個JNI的環(huán)境

JavaVMInitArgs就是虛擬機創(chuàng)建的初始化參數(shù), 這個參數(shù)里面會包含JavaVMOption

下面就是創(chuàng)建虛擬機

  1. options[0].optionString = '-Djava.class.path=.';  
  2. memset(&vm_args, 0, sizeof(vm_args));  
  3. vm_args.version = JNI_VERSION_1_4;  
  4. vm_args.nOptions = 1;  
  5. vm_args.options = options;  
  6.    
  7. // 啟動虛擬機  
  8. status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args); 

 '-Djava.class.path=.'看著眼熟吧, 這個就是傳入當前路徑, 作為JVM尋找class的用戶自定義路徑, 我們的Sample2.class就在當前路徑(當然也可以不在當前路徑, 你可以隨便修改).

vm_args.version是Java的版本, 這個應該是為了兼容以前的JDK, 可以使用舊版的JDK, 這個宏定義是在jni.h中,  有以下四種

  1. #define JNI_VERSION_1_1 0x00010001 
  2. #define JNI_VERSION_1_2 0x00010002 
  3. #define JNI_VERSION_1_4 0x00010004 
  4. #define JNI_VERSION_1_6 0x00010006   

vm_args.nOptions的含義是, 你傳入的options有多長, 我們這里就一個, 所以是1,。

vm_args.options = options把JavaVMOption傳給JavaVMInitArgs里面去。

然后就是啟動虛擬機了status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args),。

可以通過這個返回值status , 知道虛擬機是否啟動成功

  1. #define JNI_OK           0                 /* success */ 
  2. #define JNI_ERR          (-1)              /* unknown error */ 
  3. #define JNI_EDETACHED    (-2)              /* thread detached from the VM */ 
  4. #define JNI_EVERSION     (-3)              /* JNI version error */ 
  5. #define JNI_ENOMEM       (-4)              /* not enough memory */ 
  6. #define JNI_EEXIST       (-5)              /* VM already created */ 
  7. #define JNI_EINVAL       (-6)              /* invalid arguments */   

尋找class對象, 并實例化

JVM在Java中都是自己啟動的, 在C/C++中只能自己來啟動了, 啟動完之后的事情就和在Java中一樣了, 不過要使用C/C++的語法.

獲取class對象比較簡單, FindClass(env, className).

  1. cls = (*env)->FindClass(env, 'Sample2');  

在Java中的類名格式是java.lang.String, 但是className的格式有點不同, 不是使用'.'作為分割, 而是'/', 即java/lang/String.

我們知道Java中構(gòu)造函數(shù)有兩種, 一種是默認的沒有參數(shù)的, 一種是自定義的帶有參數(shù)的. 對應的在C/C++中, 有兩種調(diào)用構(gòu)造函數(shù)的方法.

調(diào)用默認構(gòu)造函數(shù)

  1. // 調(diào)用默認構(gòu)造函數(shù)  
  2. obj = (*env)->AllocObjdect(env, cls);   

構(gòu)造函數(shù)也是方法, 類似調(diào)用方法的方式.

  1. // 調(diào)用指定的構(gòu)造函數(shù), 構(gòu)造函數(shù)的名字叫做  
  2. mid = (*env)->GetMethodID(env, cls, '''()V');  
  3. obj = (*env)->NewObject(env, cls, mid);  

調(diào)用方法和修改屬性

關于方法和屬性是有兩個ID與之對應, 這兩個ID用來標識方法和屬性.

  1. jmethodID mid;  
  2. jfieldID fid; 

方法分為靜態(tài)和非靜態(tài)的, 所以對應的有

  1. mid = (*env)->GetStaticMethodID(env, cls, 'sayHello''(Ljava/lang/String;)Ljava/lang/String;');  
  2.    
  3. mid = (*env)->GetMethodID(env, cls, 'sayHello''()Ljava/lang/String;');   

上面兩個方法是同名的, 都叫sayHello, 但是簽名不同, 所以可以區(qū)分兩個方法.

JNI的函數(shù)都是有一定規(guī)律的, Static就表示是靜態(tài), 沒有表示非靜態(tài).

方法的調(diào)用如下

  1. jstring result = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid, arg);  
  2.    
  3. jstring result = (jstring)(*env)->CallObjectMethod(env, obj, mid);   

我們可以看到靜態(tài)方法是只需要class對象, 不需要實例的, 而非靜態(tài)方法需要使用我們之前實例化的對象.

屬性也有靜態(tài)和非靜態(tài), 示例中只有非靜態(tài)的.

獲取屬性ID

  1. fid = (*env)->GetFieldID(env, cls, 'name''Ljava/lang/String;');   

改屬性的值

  1. (*env)->SetObjectField(env, obj, fid, arg); // 修改屬性  

關于jstring的說明

java的String都是使用了unicode, 是雙字節(jié)的字符, 而C/C++中使用的單字節(jié)的字符,。

從C轉(zhuǎn)換為java的字符, 使用NewStringUTF方法

  1. jstring arg = (*env)->NewStringUTF(env, name);  

從java轉(zhuǎn)換為C的字符, 使用GetStringUTFChars

  1. const char* str = (*env)->GetStringUTFChars(env, result, 0); 
const char* str = (*env)->GetStringUTFChars(env, result, 0);

 編譯和運行

編譯需要頭文件, 頭文件在這兩個目錄中%JAVA_HOME%\include和%JAVA_HOME%\include\win32, 第一個是與平臺無關的, 第二個是與平臺有關的, 由于筆者的系統(tǒng)是windows, 所以是win32.

編譯的時候還要一個lib文件, 是對虛擬機的支持, 保證編譯通過.

  1. cl -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 Sample2.c %JAVA_HOME%\lib\jvm.lib  
我們可以看到在當前目錄下Sample2.exe, 運行的時候需要jvm.dll(不要將其復制到當前目錄下, 這樣不可以運行, 會導致jvm創(chuàng)建失敗)
  1. set PATH=%JAVA_HOME%\jre\bin\client\;%PATH%  
  2. Sample2  
jvm.dll在%JAVA_HOME%\jre\bin\client\目錄下, 所以我把這個目錄加入到PATH中, 然后就可以運行
  1. Result of sayHello: Hello, World!  
  2. Result of sayHello: Hello, icejoywoo! 
關于C++的說明

本示例的C++版本, 請自行下載后面的源代碼來查看, 區(qū)別不是很大.

主要是JNIEnv和JavaVM兩個對象, 在C中是結(jié)構(gòu)體, 是函數(shù)指針的集合, 在C++中結(jié)構(gòu)體擁有類的能力, 使用起來更為簡便, 與Java之間的差異更小一些.

結(jié)語

本文介紹了一個簡單的例子, 分析了其中的一些代碼, 筆者希望通過這篇文章讓大家對JNI的了解更加深入一些.

水平有限, 錯漏在所難免, 歡迎指正!
 

源代碼下載: c調(diào)用java.zip

使用方法: 參照里面的build&run.bat, 使用了%JAVA_HOME%環(huán)境變量.

注意:

  1. 動態(tài)鏈接庫和JDK都有32位和64位的區(qū)別, 使用64位系統(tǒng)的朋友, 要注意這個問題, 可能導致運行或編譯錯誤.
  2. 還要注意區(qū)分C和C++代碼, 在JNI中兩種代碼有一定的區(qū)別, 主要是env和jvm兩個地方.

參考文獻:

  1. public0821, C++調(diào)用JAVA方法詳解, http://public0821./blog/423941
  2. Scott Stricker, 用 JNI 進行 Java 編程, http://www.ibm.com/developerworks/cn/education/java/j-jni/section3.html
  3. JDK 6u30 docs, Java Native Interface Specification

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點,。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,,謹防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多