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

分享

JNI編程 —— 讓C 和Java相互調(diào)用 - android - 牛蹄印章

 techres 2011-04-27
JNI編程 —— 讓C++和Java相互調(diào)用

JNI其實是Java Native Interface的簡稱,也就是java本地接口,。它提供了若干的API實現(xiàn)了和Java和其他語言的通信(主要是C&C++),。也許不少人覺 得Java已經(jīng)足夠強大,,為什么要需要JNI這種東西呢,?我們知道Java是一種平臺無關(guān)性的語言,,平臺對于上層的java代碼來說是透明的,所以在多數(shù) 時間我們是不需要JNI的,,但是假如你遇到了如下的三種情況之一呢,?

 

  1. 你的Java代碼,,需要得到一個文件的屬性。但是你找遍了JDK幫助文檔也找不到相關(guān)的API,。
  2. 在本地還有一個別的系統(tǒng),,不過他不是Java語言實現(xiàn)的,這個時候你的老板要求你把兩套系統(tǒng)整合到一起,。
  3. 你的Java代碼中需要用到某種算法,,不過算法是用C實現(xiàn)并封裝在動態(tài)鏈接庫文件(DLL)當中的。

對于上述的三種情況,,如果沒有JNI的話,,那就會變得異常棘手了。就算找到解決方案了,,也是費時費力。其實說到底還是會增加開發(fā)和維護的成本,。

 

說了那么多一通廢話,,現(xiàn)在進入正題??催^JDK源代碼的人肯定會注意到在源碼里有很多標記成native的方法,。這些個方法只有方法簽名但是沒有方 法體。其實這些naive方法就是我們說的 java native interface,。他提供了一個調(diào)用(invoke)的接口,,然后用C或者C++去實現(xiàn)。我們首先來編寫這個“橋梁”.我自己的開發(fā)環(huán)境是 j2sdk1.4.2_15 + eclipse 3.2 + VC++ 6.0,,先在eclipse里建立一個HelloFore的Java工程,,然后編寫下面的代碼。

Java代碼
  1. package com.chnic.jni;  
  2.   
  3. public class SayHellotoCPP {  
  4.       
  5.     public SayHellotoCPP(){  
  6.     }  
  7.     public native void sayHello(String name);  
  8. }  

 

 一般的第一個程序總是HelloWorld,。今天換換口味,,把world換成一個名字。我的native本地方法有一個String的參數(shù),。會傳 遞一個name到后臺去,。本地方法已經(jīng)完成,現(xiàn)在來介紹下javah這個方法,,接下來就要用javah方法來生成一個相對應的.h頭文件,。

 

javah是一個專門為JNI生成頭文件的一個命令。CMD打開控制臺之后輸入javah回車就能看到j(luò)avah的一些參數(shù),。在這里就不多介紹 我們要用的是 -jni這個參數(shù),,這個參數(shù)也是默認的參數(shù),他會生成一個JNI式的.h頭文件,。在控制臺進入到工程的根目錄,,也就是HelloFore這個目錄,然后輸 入命令,。

Java代碼
  1. javah -jni com.chnic.jni.SayHellotoCPP  

 

命令執(zhí)行完之后在工程的根目錄就會發(fā)現(xiàn)com_chnic_jni_SayHellotoCPP.h 這個頭文件,。在這里有必要多句嘴,在執(zhí)行javah的時候,,要輸入完整的包名+類名,。否則在以后的測試調(diào)用過程中會發(fā)生java.lang.UnsatisfiedLinkError這個異常,。

 

到這里java部分算是基本完成了,接下來我們來編寫后端的C++代碼,。(用C也可以,,只不過cout比printf用起來更快些,所以這里俺偷下 懶用C++)打開VC++首先新建一個Win32 Dynamic-Link library工程,,之后選擇An empty DLL project空工程,。在這里我C++的工程是HelloEnd,把剛剛生成的那個頭文件拷貝到這個工程的根目錄里,。隨便用什么文本編輯器打開這個頭文 件,,發(fā)現(xiàn)有一個如下的方法簽名。

Cpp代碼
  1. /* 
  2.  * Class:     com_chnic_jni_SayHellotoCPP 
  3.  * Method:    sayHello 
  4.  * Signature: (Ljava/lang/String;)V 
  5.  */  
  6. JNIEXPORT void JNICALL Java_com_chnic_jni_SayHellotoCPP_sayHello  
  7.   (JNIEnv *, jobject, jstring);  

 

仔細觀察一下這個方法,,在注釋上標注類名,、方法名、簽名(Signature),,至于這個簽名是做什么用的,,我們以后再說。在這里最重要的是 Java_com_chnic_jni_SayHellotoCPP_sayHello這個方法簽名,。在Java端我們執(zhí)行 sayHello(String name)這個方法之后,,JVM就會幫我們喚醒在DLL里的Java_com_chnic_jni_SayHellotoCPP_sayHello這個方 法。因此我們新建一個C++ source file來實現(xiàn)這個方法,。

Cpp代碼
  1. #include <iostream.h>  
  2. #include "com_chnic_jni_SayHellotoCPP.h"  
  3.   
  4.   
  5. JNIEXPORT void JNICALL Java_com_chnic_jni_SayHellotoCPP_sayHello   
  6.   (JNIEnv* env, jobject obj, jstring name)  
  7. {  
  8.     const char* pname = env->GetStringUTFChars(name, NULL);  
  9.     cout << "Hello, " << pname << endl;  
  10. }  

 

因為我們生成的那個頭文件是在C++工程的根目錄不是在環(huán)境目錄,,所以我們要把尖括號改成單引號,至于VC++的環(huán)境目錄可以在 Tools->Options->Directories里設(shè)置,。F7編譯工程發(fā)現(xiàn)缺少jni.h這個頭文件,。這個頭文件可以 在%JAVA_HOME%\include目錄下找到。把這個文件拷貝到C++工程目錄,,繼續(xù)編譯發(fā)現(xiàn)還是找不到,。原來是因為在我們剛剛生成的那個頭文件 里,jni.h這個文件是被 #include <jni.h>引用進來的,,因此我們把尖括號改成雙引號#include "jni.h",,繼續(xù)編譯發(fā)現(xiàn)少了jni_md.h文件,接著在%JAVA_HOME%\include\win32下面找到那個頭文件,,放入到工程根目 錄,,F(xiàn)7編譯成功。在Debug目錄里會發(fā)現(xiàn)生成了HelloEnd.dll這個文件,。

 

這個時候后端的C++代碼也已經(jīng)完成,接下來的任務(wù)就是怎么把他們連接在一起了,,要讓前端的java程序“認識并找到”這個動態(tài)鏈接庫,,就必須把這個DLL放在windows path環(huán)境變量下面,。有兩種方法可以做到:

 

  1. 把這個DLL放到windows下面的sysytem32文件夾下面,這個是windows默認的path
  2. 復制你工程的Debug目錄,,我這里是C:\Program Files\Microsoft Visual Studio\MyProjects\HelloEnd\Debug這個目錄,,把這個目錄配置到User variable的Path下面。重啟eclipse,,讓eclipse在啟動的時候重新讀取這個path變量,。

 

比較起來,第二種方法比較靈活,,在開發(fā)的時候不用來回copy dll文件了,,節(jié)省了很多工作量,所以在開發(fā)的時候推薦用第二種方法,。在這里我們使用的也是第二種,,eclipse重啟之后打開 SayHellotoCPP這個類。其實我們上面做的那些是不是是讓JVM能找到那些DLL文件,,接下來我們要讓我們自己的java代碼“認識”這個動態(tài) 鏈接庫,。加入System.loadLibrary("HelloEnd");這句到靜態(tài)初始化塊里。

 

Java代碼
  1. package com.chnic.jni;  
  2.   
  3. public class SayHellotoCPP {  
  4.       
  5.     static{  
  6.         System.loadLibrary("HelloEnd");  
  7.     }  
  8.     public SayHellotoCPP(){  
  9.     }  
  10.     public native void sayHello(String name);  
  11.       
  12. }  

 

這樣我們的代碼就能認識并加載這個動態(tài)鏈接庫文件了,。萬事俱備,,只欠測試代碼了,接下來編寫測試代碼,。

Java代碼
  1. SayHellotoCPP shp = new SayHellotoCPP();  
  2. shp.sayHello("World");  

 

我們不讓他直接Hello,,World。我們把World傳進去,,執(zhí)行代碼,。發(fā)現(xiàn)控制臺打印出來Hello, World這句話。就此一個最簡單的JNI程序已經(jīng)開發(fā)完成,。也許有朋友會對CPP代碼里的

Cpp代碼
  1. const char* pname = env->GetStringUTFChars(name, NULL);  

 

 這句有疑問,,這個GetStringUTFChars就是JNI給developer提供的API,我們以后再講,。在這里不得不多句嘴,。

  1. 因為JNI有一個Native這個特點,一點有項目用了JNI,,也就說明這個項目基本不能跨平臺了,。
  2. JNI調(diào)用是相當慢的,在實際使用的之前一定要先想明白是否有這個必要,。
  3. 因為C++和C這樣的語言非常靈活,,一不小心就容易出錯,比如我剛剛的代碼就沒有寫析構(gòu)字符串釋放內(nèi)存,,對于java developer來說因為有了GC 垃圾回收機制,,所以大多數(shù)人沒有寫析構(gòu)函數(shù)這樣的概念,。所以JNI也會增加程序中的風險,增大程序的不穩(wěn)定性,。
 

其實在Java代碼中,,除了對本地方法標注native關(guān)鍵字和加上要加載動態(tài)鏈接庫之外,JNI基本上是對上層coder透明的,,上層coder調(diào)用那些本地方法的時候并不知道這個方法的方法體究竟是在哪里,,這個道理就像我們用JDK所提供的API一樣。所以在Java中使用JNI還是很簡單的,,相比之下在C++中調(diào)用java,,就比前者要復雜的多了。

 

現(xiàn)在來介紹下JNI里的數(shù)據(jù)類型,。在C++里,,編譯器會很據(jù)所處的平臺來為一些基本的 數(shù)據(jù)類型來分配長度,因此也就造成了平臺不一致性,,而這個問題在Java中則不存在,,因為有JVM的緣故,所以Java中的基本數(shù)據(jù)類型在所有平臺下得到 的都是相同的長度,,比如int的寬度永遠都是32位,。基于這方面的原因,,java和c++的基本數(shù)據(jù)類型就需要實現(xiàn)一些mapping,,保持一致性。下面 的表可以概括:

 

    Java類型                 本地類型                  JNI中定義的別名    
int long jint
long _int64 jlong
byte signed char jbyte
boolean unsigned char jboolean
char unsigned short jchar
short short jshort
float float jfloat
double double jdouble
Object _jobject* jobject

 

上面的表格是我在網(wǎng)上搜的,,放上來給大家對比一下,。對于每一種映射的數(shù)據(jù)類型,JNI的設(shè)計者其實已經(jīng)幫我們?nèi)『昧讼鄳膭e名以方便記憶,。如果想了解一些更加細致的信息,,可以去看一些jni.h這個頭文件,各種數(shù)據(jù)類型的定義以及別名就被定義在這個文件中,。

 

了解了JNI中的數(shù)據(jù)類型,,下面就來看這次的例子。這次我們用Java來實現(xiàn)一個前端的market(以下就用Foreground代替)用CPP來實現(xiàn)一個后端factory(以下用backend代替),。我們首先還是來編寫包含本地方法的java類,。

 

Java代碼
  1. package com.chnic.service;  
  2.   
  3. import com.chnic.bean.Order;  
  4.   
  5. public class Business {  
  6.     static{  
  7.         System.loadLibrary("FruitFactory");  
  8.     }  
  9.       
  10.     public Business(){  
  11.           
  12.     }  
  13.       
  14.     public native double getPrice(String name);  
  15.     public native Order getOrder(String name, int amount);  
  16.     public native Order getRamdomOrder();  
  17.     public native void analyzeOrder(Order order);  
  18.       
  19.     public void notification(){  
  20.         System.out.println("Got a notification.");  
  21.     }  
  22.       
  23.     public static void notificationByStatic(){  
  24.         System.out.println("Got a notification in a static method.");  
  25.     }  
  26. }  
 

這個類里面包含4個本地方法,一個靜態(tài)初始化塊加載將要生成的dll文件,。剩下的方法都是很普通的java方法,,等會在backend中回調(diào)這些方法。這個類需要一個名為Order的JavaBean。

 

Java代碼
  1. package com.chnic.bean;  
  2.   
  3. public class Order {  
  4.       
  5.     private String name = "Fruit";  
  6.     private double price;  
  7.     private int amount = 30;  
  8.       
  9.     public Order(){  
  10.           
  11.     }  
  12.   
  13.     public int getAmount() {  
  14.         return amount;  
  15.     }  
  16.    
  17.     public void setAmount(int amount) {  
  18.         this.amount = amount;  
  19.     }  
  20.   
  21.     public String getName() {  
  22.         return name;  
  23.     }  
  24.   
  25.     public void setName(String name) {  
  26.         this.name = name;  
  27.     }  
  28.   
  29.     public double getPrice() {  
  30.         return price;  
  31.     }  
  32.   
  33.     public void setPrice(double price) {  
  34.         this.price = price;  
  35.     }  
  36. }  

 

JavaBean中,,我們?yōu)閮蓚€私有屬性賦值,,方便后面的例子演示,。到此為止除了測試代碼之外的Java端的代碼就全部高調(diào)了,,接下來進行生成.h 頭文件、建立C++工程的工作,,在這里就一筆帶過,,不熟悉的朋友請回頭看第一篇。在工程里我們新建一個名為Foctory的C++ source file 文件,,去實現(xiàn)那些native方法,。具體的代碼如下。

 

Cpp代碼
  1. #include <iostream.h>  
  2. #include <string.h>  
  3. #include "com_chnic_service_Business.h"  
  4.   
  5. jobject getInstance(JNIEnv* env, jclass obj_class);  
  6.   
  7. JNIEXPORT jdouble JNICALL Java_com_chnic_service_Business_getPrice(JNIEnv* env,   
  8.                                                                    jobject obj,   
  9.                                                                    jstring name)  
  10. {  
  11.     const char* pname = env->GetStringUTFChars(name, NULL);  
  12.     cout << "Before release: "  << pname << endl;  
  13.   
  14.     if (strcmp(pname, "Apple") == 0)  
  15.     {  
  16.         env->ReleaseStringUTFChars(name, pname);  
  17.         cout << "After release: " << pname << endl;  
  18.         return 1.2;  
  19.     }   
  20.     else  
  21.     {  
  22.         env->ReleaseStringUTFChars(name, pname);  
  23.         cout << "After release: " << pname << endl;  
  24.         return 2.1;  
  25.     }     
  26. }  
  27.   
  28.   
  29. JNIEXPORT jobject JNICALL Java_com_chnic_service_Business_getOrder(JNIEnv* env,   
  30.                                                                    jobject obj,   
  31.                                                                    jstring name,   
  32.                                                                    jint amount)  
  33. {  
  34.     jclass order_class = env->FindClass("com/chnic/bean/Order");  
  35.     jobject order = getInstance(env, order_class);  
  36.       
  37.     jmethodID setName_method = env->GetMethodID(order_class, "setName""(Ljava/lang/String;)V");  
  38.     env->CallVoidMethod(order, setName_method, name);  
  39.   
  40.     jmethodID setAmount_method = env->GetMethodID(order_class, "setAmount""(I)V");  
  41.     env->CallVoidMethod(order, setAmount_method, amount);  
  42.   
  43.     return order;  
  44. }  
  45.   
  46. JNIEXPORT jobject JNICALL Java_com_chnic_service_Business_getRamdomOrder(JNIEnv* env,   
  47.                                                                          jobject obj)  
  48. {  
  49.     jclass business_class = env->GetObjectClass(obj);  
  50.     jobject business_obj = getInstance(env, business_class);  
  51.   
  52.     jmethodID notification_method = env->GetMethodID(business_class, "notification""()V");  
  53.     env->CallVoidMethod(obj, notification_method);  
  54.   
  55.     jclass order_class = env->FindClass("com/chnic/bean/Order");  
  56.     jobject order = getInstance(env, order_class);  
  57.     jfieldID amount_field = env->GetFieldID(order_class, "amount""I");  
  58.     jint amount = env->GetIntField(order, amount_field);  
  59.     cout << "amount: " << amount << endl;  
  60.     return order;  
  61. }  
  62.   
  63.   
  64. JNIEXPORT void JNICALL Java_com_chnic_service_Business_analyzeOrder (JNIEnv* env,   
  65.                                                                      jclass cls,   
  66.                                                                      jobject obj)  
  67. {  
  68.     jclass order_class = env->GetObjectClass(obj);  
  69.     jmethodID getName_method = env->GetMethodID(order_class, "getName""()Ljava/lang/String;");  
  70.     jstring name_str = static_cast<jstring>(env->CallObjectMethod(obj, getName_method));  
  71.     const char* pname = env->GetStringUTFChars(name_str, NULL);  
  72.   
  73.     cout << "Name in Java_com_chnic_service_Business_analyzeOrder: " << pname << endl;  
  74.     jmethodID notification_method_static = env->GetStaticMethodID(cls, "notificationByStatic""()V");  
  75.     env->CallStaticVoidMethod(cls, notification_method_static);  
  76.   
  77. }  
  78.   
  79. jobject getInstance(JNIEnv* env, jclass obj_class)  
  80. {  
  81.     jmethodID construction_id = env->GetMethodID(obj_class, "<init>""()V");  
  82.     jobject obj = env->NewObject(obj_class, construction_id);  
  83.     return obj;  
  84. }  
 

可以看到,,在我Java中的四個本地方法在這里全部被實現(xiàn),,接下來針對這四個方法來解釋下,一些JNI相關(guān)的API的使用方法,。先從第一個方法講起吧:

 

1.getPrice(String name)

 

這個方法是從foreground傳遞一個類型為string的參數(shù)到backend,,然后backend判斷返回相應的價格。在cpp的代碼中,, 我們用GetStringUTFChars這個方法來把傳來的jstring變成一個UTF-8編碼的char型字符串,。因為jstring的實際類型是 jobject,所以無法直接比較,。

GetStringUTFChars方法包含兩個參數(shù),,第一參數(shù)是你要處理的jstring對象,第二個參數(shù)是否需要在內(nèi)存中生成一個副本對象,。將 jstring轉(zhuǎn)換成為了一個const char*了之后,,我們用string.h中帶strcmp函數(shù)來比較這兩個字符串,如果傳來的字符串是“Apple”的話我們返回1.2,。反之返回 2.1,。在這里還要多說一下ReleaseStringUTFChars這個函數(shù),這個函數(shù)從字面上不難理解,,就是釋放內(nèi)存用的,。有點像cpp里的析構(gòu)函 數(shù),只不過Sun幫我們已經(jīng)封裝好了,。由于在JVM中有GC這個東東,,所以多數(shù)java coder并沒有寫析構(gòu)的習慣,不過在JNI里是必須的了,,否則容易造成內(nèi)存泄露,。我們在這里在release之前和之后分別打出這個字符串來看一下效果。

 

粗略的解釋完一些API之后,我們編寫測試代碼,。

 

Java代碼
  1. Business b = new Business();          
  2. System.out.println(b.getPrice("Apple"));  
 

運行這段測試代碼,,控制臺上打出

 

Before release: Apple
After release: ??
1.2

 

在release之前打印出來的是我們“需要”的Apple,release之后就成了亂碼了,。由于傳遞的是Apple,,所以得到1.2。測試成功,。

 

2. getOrder(String name, int amount)

 

在foreground中可以通過這個方法讓backend返回一個你“指定”的Order,。所謂“指定”,其實也就是指方法里的兩個參數(shù):name和amout,,在cpp的代碼在中,,會根據(jù)傳遞的兩個參數(shù)來構(gòu)造一個Order?;氐絚pp的代碼里,。

 

Java代碼
  1. jclass order_class = env->FindClass("com/chnic/bean/Order");  

 

是不是覺得這句代碼似曾相識?沒錯,,這句代碼很像我們java里寫的Class.forName(className)反射的代碼,。其實在這里 FindClass的作用和上面的forName是類似的。只不過在forName中要用完整的類名,,但是在這里必須用"/"來代替“.”,。這個方法會返 回一個jclass的對象,其實也就是我們在Java中說的類對象,。

 

Java代碼
  1. jmethodID construction_id = env->GetMethodID(obj_class, "<init>""()V");  
  2. jobject obj = env->NewObject(obj_class, construction_id);  
 

拿到"類對象"了之后,,按照Java RTTI的邏輯我們接下來就要喚醒那個類對象的構(gòu)造函數(shù)了。在JNI中,,包括構(gòu)造函數(shù)在內(nèi)的所有方法都被看成Method,。每個method都有一個特定的ID,我們通過GetMethodID這個方法就可以拿到我們想要的某一個java 方法的ID,。GetMethodID需要傳三個參數(shù),,第一個是很顯然jclass,第二個參數(shù)是java方法名,,也就是你想取的method ID的那個方法的方法名(有些繞口 ),,第三個參數(shù)是方法簽名。

 

 在這里有必要單獨來講一講這個方法簽名,,為什么要用這個東東呢,?我們知道,在Java里方法是可以被重載的,,比如我一個類里有public void a(int arg)和public void a(String arg)這兩個方法,,在這里用方法名來區(qū)分方法顯然就是行不通的了,。方法簽名包括兩部分:參數(shù)類型和返回值類型;具體的格式:(參數(shù)1類型簽名 參數(shù)2類型簽名)返回值類型簽名,。下面是java類型和年名類型的對照的一個表

 

    Java類型      對應的簽名
boolean Z
byte B
char C
shrot S
int I
long L
float F
double D
void V
Object L用/分割包的完整類名;  Ljava/lang/String;
Array [簽名       [I       [Ljava/lang/String;

 

其實除了自己對照手寫之外,,JDK也提供了一個很好用的生成簽名的工具javap,cmd進入控制臺到你要生成簽名的那個類的目錄下。在這里用 Order類打比方,,敲入: javap -s -private Order,。 所有方法簽名都會被輸出,關(guān)于javap的一些參數(shù)可以在控制臺下面輸入 javap -help查看,。(做coder的 畢竟還是要認幾個單詞的)

 

啰嗦了一大堆,,還是回到我們剛剛的getMethodID這個方法上,。因為是調(diào)用構(gòu)造函數(shù),,JNI規(guī)定調(diào)用構(gòu)造函數(shù)的時候傳遞的方法名應該為<init> ,通過javap查看 我們要的那個無參的構(gòu)造函數(shù)的方法簽是()V。得到方法簽名,,最后我們調(diào)用NewObject方法來生成一個新的對象,。

 

拿到了對象,之后我們開始為對象jobject填充數(shù)值,,還是首先拿到setXXX方法的Method ID,,之后調(diào)用Call<Type>Method來調(diào)用java方法。這里的<Type>所指的是方法的返回類型,,我們剛剛調(diào)用 的是set方法的返回值是void,,因此這里的方法也就是CallVoidMethod,這個方法的參數(shù)除了前兩個要傳入jobject和 jmethodID之外還要傳入要調(diào)用的那個方法的參數(shù),,而且要順序必須一致,,這點和Java的反射一模一樣,在這里就不多解釋,。(看到這一步是不是對 java 反射又有了自己新的理解,?)

 

終于介紹完了第二個方法,下來就是測試代碼測試,。

 

Java代碼
  1. Order o = b.getOrder("Watermelom"100);  
  2. System.out.println("java: " + o.getName());  
  3. System.out.println("java: " + o.getAmount());  

 

控制臺打出

 

java: Watermelom
java: 100

 

就此,,我們完成了第二個方法的測試。


3.getRamdomOrder()

 

這個方法會從backend得到一個隨機的Order對象(抱歉這里“Random”拼錯了),,然后再調(diào)用java中相應的通知方法來通知 foreground,。getRamdomOrder方法沒有參數(shù),但是所對應的C++方法里卻有兩個參數(shù),,一定有人會不解,。其實細心的朋友一定會發(fā) 現(xiàn),JNI里所有對應Java方法的C++ 方法都會比Java方法多兩個參數(shù),,第一個參數(shù)是我們很熟悉的JNIEnv*指針,,第二個參數(shù)有時是jobject有時是個jclass。針對這第二個參 數(shù)在這里有必要多廢話兩句。

 

其實第二個參數(shù)傳遞的是包含了native本地方法的對象或者類對象,,我們知道非靜態(tài)的方法是屬于某一個對象的,,而靜態(tài)方法是屬于類對象的,所以靜 態(tài)方法可以被所有對象共享,。有這個對象/類對象,,我們就可以很方便的操作包含了native方法的對象的一些函數(shù)了。(這句話有點繞口,,沒看明白的建議多 讀兩遍),。

 

廢話完了言歸正傳,因為getRamdomOrder不是靜態(tài)的,,所以C++相對應的參數(shù)中傳遞來的是一個jobject對象,。

 

Cpp代碼
  1. jclass business_class = env->GetObjectClass(obj);  

 

這一句不難理解,GetObjectClass方法可以得到一個對象的類對象,,這句有點像Java中的Object.class,。不熟悉的朋友建議 再去看一下Java反射機制。接下來的幾句C++代碼應該在之前的方法1和方法2中都解釋過,。早backend端會發(fā)一個“消息”給 foreground,,之后new一個新的Order類出來。接下來的三句有必要再廢話一下,。

 

Cpp代碼
  1. jfieldID amount_field = env->GetFieldID(order_class, "amount""I");  
  2. jint amount = env->GetIntField(order, amount_field);  
  3. cout << "amount: " << amount << endl;  

 

之前我為Order這個Javabean的amount的屬性設(shè)置了一個初始值為30,,其實就是為了在這里演示如何在C++中拿一個Java對象的 屬性,拿的方法和我們之前說過的調(diào)用Java方法的程序差不多,,也要先拿到一個jfieldID,,之后調(diào)用Get<type>Field方法 來取得某一個對象中的某一個屬性的數(shù)值,最后cout把他打印出來,。我們編寫測試代碼來看一下最終效果,。

 

Java代碼
  1. Business b = new Business();                   
  2. Order o2 = b.getRamdomOrder();  
  3. System.out.println(o2.getName());  

 

運行上述的測試代碼之后,控制臺上打出了

 

Got a notification.
amount: 30
Fruit

 

和我們想要的結(jié)果是一樣的,,測試成功,。

 

4.analyzeOrder(Order order)

 

這是一個靜態(tài)方法,foreground會通過這個方法傳一個Order的對象到backend去,,然后再由CPP端進行“analyze”,。在這 里我們?nèi)〕鰜韨鬟f過來的Order對象的name屬性,然后打印到控制臺上,。因為這個方法是靜態(tài)static方法,,所以相對應的C++方法中的第二個參數(shù) 也變成了jclass對象,也就是Business.class這個類對象,。第三個參數(shù)是一個jobject對象,,很明顯就是我們傳遞過來的order對 象,。

 

前5句代碼應該不難理解,就是調(diào)用getName這個方法,,然后打印出來,。因為JNI的API中并沒有提供CallStringMethod這個方 法,所以我們用CallObjectMethod這個方法來取得name這個字符串(String很明顯也是一個Object),,然后再轉(zhuǎn)型成為 jstring,。也就是下面這句代碼。

 

Cpp代碼
  1. jstring name_str = static_cast<jstring>(env->CallObjectMethod(obj, getName_method));  
 

取到了name這個字符串之后cout打印出來,,之后調(diào)用Business這個類對象中的靜態(tài)方法notificationByStatic來通知 foreground,。調(diào)用的流程以及方法和非靜態(tài)都是一樣的,只不過注意JNI中調(diào)用靜態(tài)方法的API所傳遞的一個參數(shù)是一個jclass而非 jobject(這個也不難理解,,因為靜態(tài)方法是屬于class類對象的)

 

還是編寫測試代碼測試這個方法

 

Java代碼
  1. Business b = new Business();          
  2. Order o = b.getOrder("Watermelom"100);  
  3. Business.analyzeOrder(o);  
 

控制臺上打印出

 

Name in Java_com_chnic_service_Business_analyzeOrder: Watermelom
Got a notification in a static method.

 

第一句是C++中cout打印出來的,,第二句則是Java中的靜態(tài)方法打印出來的,和我們想要的結(jié)果是一致的,。

 

 

呼~好不容易介紹完了4個方法,,最后總結(jié)一下吧。

 

  1. JNI中所提供的API遠遠不止這4個方法中所使用的API,。上面介紹的都是比較常用的,本人也不可能羅列出所有的API,。
  2. 了解了JNI編程更加有利于深入了解Java中的反射機制,,反之亦然。

 

因此如果有對JNI編程有興趣或者有更深入的需要,,可以參考一下sun的相關(guān)文檔,。在這里上傳sun提供的JNI的API手冊,還有上面例子中所用的演示代碼給大家參考,。

 

  • Backend.zip (10.2 KB)
  • 描述: CPP代碼
  • 下載次數(shù): 43
  • JNI_API.zip (38.9 KB)
  • 描述: JNI的API參考手冊
  • 下載次數(shù): 65

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

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多