jni 調用C動態庫dll/so



          轉自:http://hi.baidu.com/fengying765/blog/item/7081113e5fde53e555e7233f.html


          作為主調方的Java源程序TestJNI.java如下。

          代碼清單15-4 在Linux平臺上調用C函數的例程——TestJNI.java

          1.       public class TestJNI

          2.       {

          3.          static

          4.          {

          5.            System.loadLibrary("testjni");//載入靜態庫,test函數在其中實現

          6.          }

          7.      

          8.          private native void testjni(); //聲明本地調用

          9.         

          10.       public void test()

          11.       {

          12.         testjni();

          13.       }

          14.   

          15.       public static void main(String args[])

          16.       {

          17.         TestJNI haha = new TestJNI();

          18.         haha.test();

          19.       }

          20.    }

          TestJNI.java聲明從libtestjni.so(注意Linux平臺的動態鏈接庫文件的擴展名是.so)中調用函數testjni()。

          在Linux平臺上,遵循JNI規范的動態鏈接庫文件名必須以“lib”開頭。例如在上面的Java程序中指定的庫文件名為“testjni”,則實際的庫文件應該命名為“libtestjni.so”。

          編譯TestJNI.java,并為C程序生成頭文件:

          java TestJNI.java

          javah TestJNI

          提供testjni()函數的testjni.c源文件如下。

          代碼清單15-5 在Linux平臺上調用C函數的例程——testjni.c

                 #include <stdio.h>

                #include <TestJNI.h>  

               JNIEXPORT void JNICALL Java_TestJNI_testjni(JNIEnv *env, jobject obj){

                printf("haha---------go into c!!!\n");

               }

          編寫Makefile文件如下,JDK安裝的位置請讀者自行調整:

          libtestjni.so:testjni.o

               gcc -rdynamic -shared -o libtestjni.so testjni.o

          testjni.o:testjni.c TestJNI.h

               gcc -c testjni.c -I./ -I/usr/java/jdk1.6.0_00/include -I/usr/java/jdk1.6.0_00/include/linux

          在Makefile文件中,我們描述了最終的 libtestjin.so依賴于目標文件testjni.o,而testjni.o則依賴于testjni.c源文件和TestJNI.h頭文件。請注 意,我們在將testjni.o連接成動態鏈接庫文件時使用了“-rdynamic”選項。

          執行make命令編譯testjni.c。Linux平臺和在Windows平臺上類似,有3種方法可以讓Java程序找到并裝載動態鏈接庫文件。

          — 將動態鏈接庫文件放置在當前路徑下。

          — 將動態鏈接庫文件放置在LD_LIBRARY_PATH環境變量所指向的路徑下。注意這一點和Windows平臺稍有區別,Windows平臺參考PATH環境變量。

          — 在啟動JVM時指定選項“-Djava.library.path”,將動態鏈接庫文件放置在該選項所指向的路徑下。

          從下一節開始,我們開始接觸到在JNI框架內Java調用C程序的一些高級話題,包括如何傳遞參數、如何傳遞數組、如何傳遞對象等。

          各種類型數據的傳遞是跨平臺、跨語言互操作的永恒話題,更復雜的操作其實都可以分解為各種 基本數據類型的操作。只有掌握了基于各種數據類型的互操作,才能稱得上掌握了JNI開發。從下一節開始,環境和步驟不再是闡述的重點,將不再花費專門的篇 幅,例程中的關鍵點將成為我們關注的焦點。

          15.2.2.3 傳遞字符串

          到目前為止,我們還沒有實現Java程序向C程序傳遞參數,或者C程序向Java程序傳遞參數。本例程將由Java程序向C程序傳入一個字符串,C程序對該字符串轉成大寫形式后回傳給Java程序。

          Java源程序如下。

          代碼清單15-6 在Linux平臺上調用C函數的例程——Sample1

                public class Sample1

                {

                public native String stringMethod(String text);

               public static void main(String[] args)

               {

                   System.loadLibrary("Sample1");

                    Sample1 sample = new Sample1();

                    String text   = sample.stringMethod("Thinking In Java");

                 System.out.println("stringMethod: " + text);

              }

          }

          Sample1.java以“Thinking In Java”為參數調用libSample1.so中的函數stringMethod(),在得到返回的字符串后打印輸出。

          Sample1.c的源程序如下。

          代碼清單15-7 在Linux平臺上調用C函數的例程——Sample1.c

                #include <Sample1.h>

                #include <string.h>   

                JNIEXPORT jstring JNICALL Java_Sample1_stringMethod(JNIEnv *env, jobject obj, jstring string)

                {

                    const char *str = (*env)->GetStringUTFChars(env, string, 0);

                    char cap[128];

                    strcpy(cap, str);

                    (*env)->ReleaseStringUTFChars(env, string, str);

                 int i=0;

                 for(i=0;i<strlen(cap);i++)

                   *(cap+i)=(char)toupper(*(cap+i));

                 return (*env)->NewStringUTF(env, cap);

              }

          首先請注意函數頭部分,函數接收一個jstring類 型的輸入參數,并輸出一個jstring類型的參數。jstring是jni.h中定義的數據類型,是JNI框架內特有的字符串類型,因為jni.h在 Sample1.h中被引入,因此在Sample1.c中無須再次引入。

          程序的第4行是從JNI調用上下文中獲取UTF編碼的輸入字符,將其放在指針str所指向 的一段內存中。第9行是釋放這段內存。第13行是將經過大寫轉換的字符串予以返回,這一句使用了NewStringUTF()函數,將C語言的字符串指針 轉換為JNI的jstring類型。JNIEnv也是在jni.h中定義的,代表JNI調用的上下文,GetStringUTFChars()、 ReleaseStringUTFChars()和NewStringUTF()均是JNIEnv的函數。

          15.2.2.4 傳遞整型數組

          本節例程將首次嘗試在JNI框架內啟用數組:C程序向Java程序返回一個定長的整型數組成的數組,Java程序將該數組打印輸出。

          Java程序的源代碼如下。

          代碼清單15-8 在Linux平臺上調用C函數的例程——Sample2

                 public class Sample2

                {

               public native int[] intMethod();

                public static void main(String[] args)

                {

                   System.loadLibrary("Sample2");

                   Sample2 sample=new Sample2();

                     int[] nums=sample.intMethod();

                 for(int i=0;i<nums.length;i++)

                System.out.println(nums[i]);

             }

          }

          Sample2.java調用libSample2.so中的函數intMethod()。Sample2.c的源代碼如下。

          代碼清單15-9 在Linux平臺上調用C函數的例程——Sample2.c

                #include <Sample2.h>

               

                JNIEXPORT jintArray JNICALL Java_Sample2_intMethod(JNIEnv *env, jobject obj)

                 {

                inti = 1;

                 jintArray array;//定義數組對象

                array = (*env)-> NewIntArray(env, 10);

                for(; i<= 10; i++)

                     (*env)->SetIntArrayRegion(env, array, i-1, 1, &i);

            

                 /* 獲取數組對象的元素個數 */

              int len = (*env)->GetArrayLength(env, array);

                 /* 獲取數組中的所有元素 */

              jint* elems = (*env)-> GetIntArrayElements(env, array, 0);

             for(i=0; i<len; i++)

                 printf("ELEMENT %d IS %d\n", i, elems[i]);

             return array;

              }

          Sample2.c涉及了兩個jni.h定義的整型數 相關的數據類型:jint和jintArray,jint是在JNI框架內特有的整數類型。程序的第7行開辟出一個長度為10 的jint數組。然后依次向該數組中放入元素1-10。第11行至第16行不是程序的必須部分,純粹是為了向讀者們演示GetArrayLength() 和GetIntArrayElements()這兩個函數的使用方法,前者是獲取數組長度,后者則是獲取數組的首地址以便于遍歷數組。

          15.2.2.5 傳遞字符串數組

          本節例程是對上節例程的進一步深化:雖然仍然是傳遞數組,但是數組的基類換成了字符串這樣一種對象數據類型。Java程序將向C程序傳入一個包含中文字符的字符串,C程序并沒有處理這個字符串,而是開辟出一個新的字符串數組返回給Java程序,其中還包含兩個漢字字符串。

          Java程序的源代碼如下。

          代碼清單15-10 在Linux平臺上調用C函數的例程——Sample3

                public class Sample3

                {

                 public native String[] stringMethod(String text);

               

                public static void main(String[] args) throws java.io.UnsupportedEncodingException

                 {

                   System.loadLibrary("Sample3");

                     Sample3 sample = new Sample3();

                     String[] texts = sample.stringMethod("java編程思想");

                 for(int i=0;i<texts.length;i++)

                  {

                   texts[i]=new String(texts[i].getBytes("ISO8859-1"),"GBK");

                    System.out.print( texts[i] );

                 }

                 System.out.println();

             }

              }

          Sample3.java調用libSample3.so中的函數stringMethod()。Sample3.c的源代碼如下:

          代碼清單15-11 在Linux平臺上調用C函數的例程——Sample3.c

                 #include <Sample3.h>

                #include <string.h>

                #include <stdlib.h>

                

                #define ARRAY_LENGTH 5
              

               JNIEXPORT jobjectArray JNICALL Java_Sample3_stringMethod(JNIEnv *env, jobject obj, jstring string)

                 {   

                    jclass objClass = (*env)->FindClass(env, "java/lang/String");

                 jobjectArray texts= (*env)->NewObjectArray(env, (jsize)ARRAY_LENGTH, objClass, 0);

                 jstring jstr;

                  char* sa[] = { "Hello,", "world!", "JNI", "很", "好玩" };

                 int i=0;

                 for(;i<ARRAY_LENGTH;i++)

                  {

                    jstr = (*env)->NewStringUTF( env, sa[i] );

                   (*env)->SetObjectArrayElement(env, texts, i, jstr);//必須放入jstring

                 }

                 return texts;

             }

          第9、10行是我們需要特別關注的地方:JNI框架并 沒有定義專門的字符串數組,而是使用jobjectArray——對象數組,對象數組的基類是jclass,jclass是JNI框架內特有的類型,相當 于Java語言中的Class類型。在本例程中,通過FindClass()函數在JNI上下文中獲取到java.lang.String的類型 (Class),并將其賦予jclass變量。

          在例程中我們定義了一個長度為5的對象數組texts,并在程序的第18行向其中循環放入預先定義好的sa數組中的字符串,當然前置條件是使用NewStringUTF()函數將C語言的字符串轉換為jstring類型。

          本例程的另一個關注點是C程序向Java程序傳遞的中文字符,在Java程序中能否正常顯 示的問題。在筆者的試驗環境中,Sample3.c是在Linux平臺上編輯的,其中的中文字符則是用支持GBK的輸入法輸入的,而Java程序采用 ISO8859_1字符集存放JNI調用的返回字符,因此在“代碼清單15-10在Linux平臺上調用C函數的例程——Sample3”的第14行中將 其轉碼后輸出。

          15.2.2.6 傳遞對象數組

          本節例程演示的是C程序向Java程序傳遞對象數組,而且對象數組中存放的不再是字符串,而是一個在Java中自定義的、含有一個topic屬性的MailInfo對象類型。

          MailInfo對象定義如下。

          代碼清單15-12 在Linux平臺上調用C函數的例程——MailInfo

               public class MailInfo {

                public String topic;

               public String getTopic()

              {

                   return this.topic;

                }

          public void setTopic(String topic)

             {

              this.topic=topic;

             }

             }

          Java程序的源代碼如下。

          代碼清單15-13 在Linux平臺上調用C函數的例程——Sample4

                 public class Sample4

                {

                public native MailInfo[] objectMethod(String text);

                public static void main(String[] args)

                {

                    System.loadLibrary("Sample4");

                  Sample4 sample = new Sample4();

                    MailInfo[] mails = sample.objectMethod("Thinking In Java");

                 for(int i=0;i<mails.length;i++)

                   System.out.println(mails[i].topic);

             }

          }

          Sample4.java調用libSample4.so中的objectMethod()函數。Sample4.c的源代碼如下。

          代碼清單15-14 在Linux平臺上調用C函數的例程——Sample4.c

                 #include <Sample4.h>

               #include <string.h>

                #include <stdlib.h>

              

                #define ARRAY_LENGTH 5

             

                JNIEXPORT jobjectArray JNICALL Java_Sample4_objectMethod(JNIEnv *env, jobject obj, jstring string)

                 {  

                   jclass objClass = (*env)->FindClass(env, "java/lang/Object");

                 jobjectArray mails= (*env)->NewObjectArray(env, (jsize)ARRAY_LENGTH, objClass, 0);

                 jclass objectClass = (*env)->FindClass(env, "MailInfo");

                 jfieldID topicFieldId = (*env)->GetFieldID(env, objectClass, "topic", "Ljava/lang/String;");

              

                 int i=0;

                 for(;i<ARRAY_LENGTH;i++)

              {

                (*env)->SetObjectField(env, obj, topicFieldId, string);

                   (*env)->SetObjectArrayElement(env, mails, i, obj);

                 }

                

              return mails;

             }

          程序的第9、10行讀者們應該不會陌生,在上一節的例 程中已經出現過,不同之處在于這次通過FindClass()函數在JNI上下文中獲取的是java.lang.Object的類型(Class),并將 其作為基類開辟出一個長度為5的對象數組,準備用來存放MailInfo對象。

          程序的第12、13行的目的則是創建一個jfieldID類型的變量,在JNI中,操作對 象屬性都是通過jfieldID進行的。第12行首先查找得到MailInfo的類型(Class),然后基于這個jclass進一步獲取其名為 topic的屬性,并將其賦予jfieldID變量。

          程序的第18、19行的目的是循環向對象數組中放入jobject對象。 SetObjectField()函數屬于首次使用,該函數的作用是向jobject的屬性賦值,而值的內容正是Java程序傳入的jstring變量 值。請注意在向對象屬性賦值和向對象數組中放入對象的過程中,我們使用了在函數頭部分定義的jobject類型的環境參數obj作為中介。至此,JNI框 架固有的兩個環境入參env和obj,我們都有涉及。

          posted on 2011-07-18 18:36 hijackwust 閱讀(4207) 評論(0)  編輯  收藏


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          <2011年7月>
          262728293012
          3456789
          10111213141516
          17181920212223
          24252627282930
          31123456

          導航

          統計

          常用鏈接

          留言簿(6)

          隨筆檔案(57)

          友情鏈接

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 横峰县| 蒲江县| 阿城市| 三台县| 黎川县| 工布江达县| 新建县| 昭觉县| 西乌珠穆沁旗| 迁安市| 滕州市| 济阳县| 嘉禾县| 日土县| 葵青区| 山丹县| 正镶白旗| 济宁市| 盖州市| 佳木斯市| 监利县| 凤城市| 吴旗县| 湘阴县| 阜阳市| 巴马| 凤庆县| 七台河市| 米泉市| 龙州县| 通化市| 平昌县| 宁海县| 通海县| 客服| 武强县| 南安市| 章丘市| 奉节县| 八宿县| 会宁县|